zoukankan      html  css  js  c++  java
  • 设计模式--观察者模式

    1. 概述

      有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

    2. 解决的问题

      将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。

    3. 模式中的角色

      3.1 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

      3.2 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

      3.3 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

      3.4 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

    4. 模式解读

      4.1 观察者模式的代码

     1  /// <summary>
     2     /// 抽象主题类
     3     /// </summary>
     4     public abstract class Subject
     5     {
     6         private IList<Observer> observers = new List<Observer>();
     7 
     8         /// <summary>
     9         /// 增加观察者
    10         /// </summary>
    11         /// <param name="observer"></param>
    12         public void Attach(Observer observer)
    13         {
    14             observers.Add(observer);
    15         }
    16 
    17         /// <summary>
    18         /// 移除观察者
    19         /// </summary>
    20         /// <param name="observer"></param>
    21         public void Detach(Observer observer)
    22         {
    23             observers.Remove(observer);
    24         }
    25 
    26         /// <summary>
    27         /// 向观察者(们)发出通知
    28         /// </summary>
    29         public void Notify()
    30         {
    31             foreach (Observer o in observers)
    32             {
    33                 o.Update();
    34             }
    35         }
    36     }
    37 
    38     /// <summary>
    39     /// 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
    40     /// </summary>
    41     public abstract class Observer
    42     {
    43         public abstract void Update();
    44     }
    45 
    46     /// <summary>
    47     /// 具体观察者或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
    48     /// </summary>
    49     public class ConcreteSubject : Subject
    50     {
    51         private string subjectState;
    52 
    53         /// <summary>
    54         /// 具体观察者的状态
    55         /// </summary>
    56         public string SubjectState
    57         {
    58             get { return subjectState; }
    59             set { subjectState = value; }
    60         }
    61     }
    62 
    63     /// <summary>
    64     /// 具体观察者,实现抽象观察者角色所要求的更新接口,已是本身状态与主题状态相协调
    65     /// </summary>
    66     public class ConcreteObserver : Observer
    67     {
    68         private string observerState;
    69         private string name;
    70         private ConcreteSubject subject;
    71 
    72         /// <summary>
    73         /// 具体观察者用一个具体主题来实现
    74         /// </summary>
    75         public ConcreteSubject Subject
    76         {
    77             get { return subject; }
    78             set { subject = value; }
    79         }
    80 
    81         public ConcreteObserver(ConcreteSubject subject, string name)
    82         {
    83             this.subject = subject;
    84             this.name = name;
    85         }
    86 
    87         /// <summary>
    88         /// 实现抽象观察者中的更新操作
    89         /// </summary>
    90         public override void Update()
    91         {
    92             observerState = subject.SubjectState;
    93             Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
    94         }
    95     }

      4.2 客户端代码

     1 class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             // 具体主题角色通常用具体自来来实现
     6             ConcreteSubject subject = new ConcreteSubject();
     7 
     8             subject.Attach(new ConcreteObserver(subject, "Observer A"));
     9             subject.Attach(new ConcreteObserver(subject, "Observer B"));
    10             subject.Attach(new ConcreteObserver(subject, "Observer C"));
    11 
    12             subject.SubjectState = "Ready";
    13             subject.Notify();
    14 
    15             Console.Read();
    16         }
    17     }

    以上内容来源于网络,下面是我在实际应用中的例子:

    公司的一个需求,具体是,我的一个类变化了,我需要了解到这个类的变化,并且得到他的id,和模块名称(在本demo中没有体现),那么就用到了观察者模式!

    首先,我们的业务类有一个顶层的接口,所有的业务类都实现了这个接口

    package com.sun.Observer;
    
    import java.io.Serializable;
    
    public interface Entity<PK extends Serializable> extends Serializable {
    
        void setId(PK id);
    
        PK getId();
    }

     定义一个具体的业务类,方便测试

    package com.sun.Observer;
    
    public class Student implements Entity<String> {
    
        private static final long serialVersionUID = 1L;
        private String id;
        private String name;
        private Integer age;
    
        @Override
        public void setId(String id) {
            this.id = id;
        }
    
        @Override
        public String getId() {
            return id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }

     定义一个观察者

    package com.sun.Observer;
    
    import java.util.Observable;
    import java.util.Observer;
    
    public class TstObServer implements Observer{
        
        @Override
        public void update(Observable observ, java.lang.Object o) {
            EntityEventBlock eeb = (EntityEventBlock) observ;
            System.out.println("捕获业务对象变动数 = "+ eeb.getChangeEvents().size());
            for (EntityChangeEvent event : eeb.getChangeEvents()) {
                System.out.println("entityCls:[" + event.getEntityCls() + "]"
                        + ",id:[" + event.getId() + "]"
                        + ",变动类型:[" + (event.getType() == EntityChangeEvent.TYPE_ADD ? "新增" :
                                event.getType() == EntityChangeEvent.TYPE_UPDATE ? "更新" :
                                event.getType() == EntityChangeEvent.TYPE_DELETE ? "删除" : "") + "]");
            }
        }
    
    }

     监听实体变化的辅助类

    package com.sun.Observer;
    
    import java.io.Serializable;
    
    import org.apache.commons.lang3.StringUtils;
    
    public class EntityChangeEvent {
        public static final int TYPE_ADD = 1;
        public static final int TYPE_UPDATE = 2;
        public static final int TYPE_DELETE = 3;
    
        private Class<?> entityCls;
        private Serializable id;
        private int type;
    
        public EntityChangeEvent(Class<?> entityCls, Serializable id, int type) {
            this.id = id;
            this.type = type;
            this.entityCls = entityCls;
        }
    
        public Class<?> getEntityCls() {
            return entityCls;
        }
    
        public Serializable getId() {
            return id;
        }
    
        public String getIdStr() {
            return id != null ? id.toString() : null;
        }
    
        public void setId(Serializable id) {
            this.id = id;
        }
    
        public int getType() {
            return type;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!EntityChangeEvent.class.isInstance(obj)) {
                return false;
            }
            EntityChangeEvent otherObj = (EntityChangeEvent) obj;
            return (StringUtils.equals(getIdStr(), otherObj.getIdStr()));
        }
    
        @Override
        public int hashCode() {
            if (id != null) {
                return id.hashCode();
            }
            return super.hashCode();
        }
    
    }

    通知主题

     1 package com.sun.Observer;
     2 
     3 import java.io.Serializable;
     4 import java.util.ArrayList;
     5 import java.util.List;
     6 import java.util.Observable;
     7 import java.util.Observer;
     8 
     9 public class EntityEventBlock extends Observable {
    10     private List<EntityChangeEvent> changeEvents = new ArrayList<EntityChangeEvent>();
    11 
    12     public void setObservers(List<Observer> observers) {//这里可以注入进来observer
    13         for (Observer observer : observers) {
    14             this.addObserver(observer);
    15         }
    16     }
    17 
    18     public static synchronized EntityEventBlock instance() {
    19         EntityEventBlock eventBlock = new EntityEventBlock();// from httpSession
    20         return eventBlock;
    21     }
    22 
    23     public void makeDelEvent(Class entityCls, Serializable id) {
    24         makeChangeEvent(entityCls, id, EntityChangeEvent.TYPE_DELETE);
    25     }
    26 
    27     public void makeAddEvent(Class entityCls, Serializable id) {
    28         makeChangeEvent(entityCls, id, EntityChangeEvent.TYPE_ADD);
    29     }
    30 
    31     public void makeUpdateEvent(Class entityCls, Serializable id) {
    32         makeChangeEvent(entityCls, id, EntityChangeEvent.TYPE_UPDATE);
    33     }
    34 
    35     private void makeChangeEvent(Class entityCls, Serializable id, int changeType) {
    36         EntityChangeEvent e = new EntityChangeEvent(entityCls, id, changeType);
    37         if (changeEvents.contains(e)) {
    38             changeEvents.remove(e);
    39         }
    40         changeEvents.add(e);
    41         this.setChanged();
    42         this.notifyObservers(); 
    43     }
    44 
    45     public List<EntityChangeEvent> getChangeEvents() {
    46         return changeEvents;
    47     }
    48 
    49     public void setChangeEvents(List<EntityChangeEvent> changeEvents) {
    50         this.changeEvents = changeEvents;
    51     }
    52 }

     测试

     1 package com.sun.Observer;
     2 
     3 import java.util.Observer;
     4 import java.util.UUID;
     5 
     6 
     7 public class ObserverDemo {
     8     private static EntityEventBlock e = EntityEventBlock.instance();
     9     static {
    10         Observer o = new TstObServer();
    11         e.addObserver(o);
    12     }
    13     
    14     public static void main(String[] args) {
    15         
    16         Student s1 = new Student();
    17         s1.setId(UUID.randomUUID().toString());
    18         add(s1);
    19         Student s2 = new Student();
    20         s2.setId(UUID.randomUUID().toString());
    21         add(s2);
    22 
    23     }
    24 
    25     static void update(Entity entity) {
    26         e.makeUpdateEvent(entity.getClass(), entity.getId());
    27 
    28     }
    29 
    30     static void add(Entity entity) {
    31         e.makeAddEvent(entity.getClass(), entity.getId());
    32     }
    33 
    34     static void delete(Entity entity) {
    35         e.makeDelEvent(entity.getClass(), entity.getId());
    36     }
    37 }
  • 相关阅读:
    iframe子页面获取父页面元素和window对象
    jQuery使用blur()方法触发两次的解决方法
    java使用freemarker生成word
    java实现下载文件
    IE11中实现颜色渐变
    MYSQL中INET_ATON()函数
    数据库SQL实战(1)
    MYSQL表中向SET类型的字段插入值时值之间不能有空格
    MYSQL表中设置字段类型为TIMESTAMP时的注意事项
    SQL中判断值是否为NULL
  • 原文地址:https://www.cnblogs.com/sun-space/p/5712477.html
Copyright © 2011-2022 走看看