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

    设计模式--观察者模式

    1 概述


    1.1 定义
    观察者模式(Observer Design),也叫发布订阅模式:定义对象间一对多的依赖关系,使得每当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新。

    1.2 应用
    消息队列的处理机制,如EJB的消息队列。(原理基本相同)

    1.3 类图
    enter description here

    组合模式涉及的角色如下:

    • Subject被观察者抽象角色:定义被观察者需要实现的行为,如动态的添加、删除观察者、通知观察者。
    • ConcreteSubject被观察者具体角色:被观察者抽象角色的具体实现,定义自己的业务逻辑,并通知观察者。
    • Observer观察者抽象角色:定义观察者的抽象行为。
    • ConcteteObserver观察者具体角色:具体的观察者,实现自己的业务逻辑。

    2 详解

     1 public interface Observer {
     2     void update(Object arg);
     3 }
     4 
     5 public class ConcreteObserver implements Observer {
     6     @Override
     7     public void update(Object arg) {
     8         System.out.println("I accept arg: " + arg);
     9     }
    10 }
    11 
    12 public abstract class Subject {
    13     // 定义一个观察者数组(Vector线程安全)
    14     private Vector<Observer> vector = new Vector<>();
    15 
    16     // 添加观察者
    17     public synchronized void addObserver(Observer observer) {
    18         vector.addElement(observer);
    19     }
    20 
    21     // 删除一个观察者
    22     public synchronized void removeObserver(Observer observer) {
    23         vector.removeElement(observer);
    24     }
    25 
    26     // 清空观察者
    27     public synchronized void clear(Observer observer) {
    28         vector.removeAllElements();
    29     }
    30 
    31     // 通知观察者
    32     public synchronized void notifyObservers(Object arg) {
    33         for(Observer observer : vector) {
    34             observer.update(arg);
    35         }
    36     }
    37 }
    38 
    39 public class ConcreteSubject extends Subject {
    40     // 定义自己的业务逻辑并通知观察者
    41     public void operate() {
    42         System.out.println("I do some operations and notify observes");
    43         notifyObservers(null);
    44     }
    45 }
    46 
    47 public class Client {
    48     public static void main(String[] args) {
    49         // 创建观察者
    50         Observer observer = new ConcreteObserver();
    51         Observer observer1 = new ConcreteObserver();
    52 
    53         // 创建被观察者并添加观察者
    54         ConcreteSubject subject = new ConcreteSubject();
    55         subject.addObserver(observer);
    56         subject.addObserver(observer1);
    57 
    58         // 被观察者活动并通知观察者
    59         subject.operate();
    60     }
    61 }output:
    62 I do some operations and notify observes
    63 I accept arg: null
    64 I accept arg: null

    JDK中自带了有观察者模式:java.util.Observable是被观察者抽象角色,java.util.Observer是观察者抽象角色。你只需要实现自己的观察者与被观察者就可使用。

     1 public interface Observer {
     2     void update(Observable o, Object arg);
     3 }
     4 
     5 public class Observable {
     6     // 标识:标识被观察者是否改变
     7     private boolean changed = false;
     8     private Vector<Observer> obs;
     9 
    10     public Observable() {
    11         obs = new Vector<>();
    12     }
    13     // 添加观察者
    14     public synchronized void addObserver(Observer o) {
    15         if (o == null)
    16             throw new NullPointerException();
    17         if (!obs.contains(o)) {
    18             obs.addElement(o);
    19         }
    20     }
    21     // 删除观察者
    22     public synchronized void deleteObserver(Observer o) {
    23         obs.removeElement(o);
    24     }
    25     // 清空观察者
    26     public synchronized void deleteObservers() {
    27         obs.removeAllElements();
    28     }
    29     // 通知所有观察者
    30     public void notifyObservers() {
    31         notifyObservers(null);
    32     }
    33     public void notifyObservers(Object arg) {
    34         /**
    35          * 一个观察者缓冲区,用于并发访问被观察者时,留住观察者列表的当前状态,这样能够减少该对象加锁的时
    36          * 间,这种处理方式其实也算是一种设计模式,即备忘录模式。
    37          */
    38         Object[] arrLocal;
    39     /**
    40      * 同步块:获得所有的观察者,以通知所有的观察者。这里使用同步块能够避免在通知中有人修改观察者
    41      * 集合。如上所诉,使用观察者缓冲区能够减少该对象的加锁时间。
    42      */
    43         synchronized (this) {
    44             if (!changed)
    45                 return;
    46             arrLocal = obs.toArray();
    47             clearChanged();
    48         }
    49 
    50         for (int i = arrLocal.length-1; i>=0; i--)
    51             ((Observer)arrLocal[i]).update(this, arg);
    52     }
    53     // 设置该被观察者改变标识为true
    54     protected synchronized void setChanged() {
    55         changed = true;
    56     }
    57     // 设置该被观察者改变标识为false
    58     protected synchronized void clearChanged() {
    59         changed = false;
    60     }
    61 
    62     public synchronized boolean hasChanged() {
    63         return changed;
    64     }
    65     
    66     public synchronized int countObservers() {
    67         return obs.size();
    68     }

    小结一下

    1. 观察者模式就是使用的回调机制,它更注重发布-订阅。被观察者一般有许多的观察者,被观察者会通知所有的观察者。在现实生活中就像微信工作号一样,只要你关注了它,它就会给你推送消息。
    2. JDK中的观察者能够是线程安全的。但是当我们观察者处理通知时间较长时,会发生阻塞,因为其通知观察者是采用了同步的方式,而不是异步方式。
    3. 不足:观察者模式中被观察者是一个抽象类,只能单继承,当然可以使用设配器模式。观察者模式中的观察者响应是固定的,即update()方法。

    3 应用

     1 public class People implements Observer {
     2     private WeChat weChat;
     3     private String name;
     4 
     5     People(String name, WeChat weChat) {
     6         this.name = name;
     7         this.weChat = weChat;
     8     }
     9 
    10     // 订阅微信公众号
    11     public boolean order (String name) {
    12         return weChat.order(this, name);
    13     }
    14 
    15     // 接受微信公众号发送的消息
    16     public void recieveMessage(Observable o, Object arg) {
    17         System.out.println(name + "接收到微信公众号「" + o + "」的信息:" + arg);
    18     }
    19 
    20     @Override
    21     public void update(Observable o, Object arg) {
    22         recieveMessage(o, arg);
    23     }
    24 }
    25 
    26 /**
    27  * 微信公众号
    28  */
    29 public class WeChatPublicMessage extends Observable {
    30     private String name;
    31 
    32     WeChatPublicMessage(String name) {
    33         super();
    34         this.name = name;
    35     }
    36 
    37     // 发布消息
    38     public void publish(String message) {
    39         setChanged();
    40         notifyObservers(message);
    41     }
    42     
    43     @Override
    44     public String toString() {
    45         return name;
    46     }
    47 
    48 }
    49 
    50 public class WeChat {
    51     public static HashMap<String, WeChatPublicMessage> map = new HashMap<>();
    52 
    53     // 订阅微信公众号
    54     public boolean order(People people, String name) {
    55         WeChatPublicMessage weChatPublicMessage = map.get(name);
    56         if(weChatPublicMessage == null) {
    57             return false;
    58         }
    59         // 该微信公众号添加订阅人
    60         weChatPublicMessage.addObserver(people);
    61         return true;
    62     }
    63 }
    64 
    65 public class Client {
    66     public static void main(String[] args) {
    67         WeChatPublicMessage weChatPublicMessage = new WeChatPublicMessage("小道消息");
    68         WeChatPublicMessage weChatPublicMessage1 = new WeChatPublicMessage("侠客岛");
    69         WeChatPublicMessage weChatPublicMessage2 = new WeChatPublicMessage("财新网");
    70         WeChat.map.put("小道消息", weChatPublicMessage);
    71         WeChat.map.put("侠客岛", weChatPublicMessage1);
    72         WeChat.map.put("财新网", weChatPublicMessage2);
    73 
    74         People people = new People("张三", new WeChat());
    75         People people1 = new People("李四", new WeChat());
    76 
    77         people.order("小道消息");
    78         people.order("财新网");
    79         people.order("侠客岛");
    80         people1.order("财新网");
    81 
    82         weChatPublicMessage.publish("今天三星S8发布");
    83         weChatPublicMessage1.publish("韩美举行大规模的军演,朝鲜发出严正抗议");
    84         weChatPublicMessage2.publish("第一季度中国GDP同比增长6.6%");
    85     }
    86 }output:
    87 张三接收到微信公众号「小道消息」的信息:今天三星S8发布
    88 张三接收到微信公众号「侠客岛」的信息:韩美举行大规模的军演,朝鲜发出严正抗议
    89 李四接收到微信公众号「财新网」的信息:第一季度中国GDP同比增长6.6%
    90 张三接收到微信公众号「财新网」的信息:第一季度中国GDP同比增长6.6%
    View Code
  • 相关阅读:
    剑指offer--面试题14--收获
    剑指offer--面试题14
    剑指offer--面试题13
    剑指offer--面试题12
    剑指offer--面试题11
    高质量代码
    剑指offer--面试题10--相关
    剑指offer--面试题10
    位运算及其操作
    负数在计算机中的表示方法
  • 原文地址:https://www.cnblogs.com/maying3010/p/6647583.html
Copyright © 2011-2022 走看看