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 }
  • 相关阅读:
    安装lnmp 时如何修改数据库数据存储地址及默认访问地址
    ubuntu 设置root用户密码并实现root用户登录
    解决ubuntu 远程连接问题
    linux 搭建FTP服务器
    PHP 根据ip获取对应的实际地址
    如何发布自己的composer包
    使用composer安装composer包报Your requirements could not be resolved to an installable set of packages
    laravel 框架配置404等异常页面
    使用Xshell登录linux服务器报WARNING! The remote SSH server rejected X11 forwarding request
    IoTSharp 已支持国产松果时序数据库PinusDB
  • 原文地址:https://www.cnblogs.com/sun-space/p/5712477.html
Copyright © 2011-2022 走看看