zoukankan      html  css  js  c++  java
  • 观察者模式-将消息通知给观察者

    公号:码农充电站pro
    主页:https://codeshellme.github.io

    观察者模式Observer Design Pattern)也被称为发布订阅模式Publish-Subscribe Design Pattern),主要用于更好的解决向对象通知消息的问题。

    观察者模式定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。

    观察者模式可以用很多称呼来描述,比如:

    • Subject-Observer:主题-观察者。
    • Publisher-Subscriber:发布者-订阅者。
    • Producer-Consumer:生产者-消费者。

    1,订阅报纸

    我们以订阅报纸为例,来描述 SubjectObserver 之间的关系。

    Subject 相当于报社Observer 就相当于订阅报纸的用户

    • 从报社的角度来说:
      • 报社可向用户提供新闻消息,用户可以订阅报社的报纸,也可以取消订阅。
      • 报社记录了所有订阅报纸的用户名单
        • 如果有用户订阅了报纸,报社就会在名单中加入他的名字。
        • 如果有用户取消了报纸,报社就会从名单中删去他的名字。
      • 当报社有了新闻,会主动将新闻通知给它的所有订阅用户。
      • 没有订阅的用户,报社则不会去通知他。
    • 从用户的角度来说:
      • 如果用户订阅了报社的报纸,当报社有了新闻,他就会收到报社的消息。
      • 如果用户取消了报社的报纸,当报社有了新闻,报社也不会通知他。

    SubjectObserver一对多关系

    在这里插入图片描述

    2,观察者模式类图

    这里直接给出观察者模式的类图,这是最经典的实现方式,其它的变种都可以在它的基础上加以改进。

    在这里插入图片描述

    从类图中可以知道,Subject 是一个接口,有三个抽象方法:

    • registerObserver:用于注册 observer
    • removeObserver:用于移除 observer
    • notifyObservers:当有了消息,通知所有 observers

    Observer 也是一个接口,有一个抽象方法:

    • update:当 subject 发来新消息时,用于更新消息。

    3,实现观察者模式

    下面我们来用代码实现观察者模式。

    首先是两个接口 SubjectObserver

    interface Subject {
        void registerObserver(Observer o);
        void removeObserver(Observer o);
        void notifyObservers(String info);
    }
    
    interface Observer {
        void update(String info);
    }
    

    这两个接口完全是按照类图中的内容来实现的,其中变量 info 的类型可以根据实际的应用场景来定。

    再实现 ConcreteSubjectConcreteObserver

    class ConcreteSubject implements Subject {
    
    	// 用于存放 observer
        private final ArrayList<Observer> observers;
    
        public ConcreteSubject() {
            observers = new ArrayList();
        }
    
        public void registerObserver(Observer o) {
            observers.add(o);
        }
    
        public void removeObserver(Observer o) {
            observers.remove(o);
        }
    
        public void notifyObservers(String info) {
            for (Observer o: observers) {
                o.update(info);
            }
        }
    }
    
    class ConcreteObserver implements Observer {
        private final String name;
    
        public ConcreteObserver(String name) {
            this.name = name;
        }
    
        public void update(String info) {
            System.out.println(this.name + " get info: " + info);
        }
    }
    

    ConcreteSubject 中的 observers 用于存储观察者,这里使用的类型是 ArrayList,也可以根据实际的应用场景来选择。

    ConcreteObserver 中的 name 只是为了表示不同的观察者,观察者在收到消息后,将消息打印在控制台。

    测试这两个类:

    // 创建被观察者
    ConcreteSubject s = new ConcreteSubject();
    
    // 创建两个观察者
    ConcreteObserver o1 = new ConcreteObserver("o1");
    ConcreteObserver o2 = new ConcreteObserver("o2");
    
    // 注册观察者
    s.registerObserver(o1); // 注册 o1
    s.registerObserver(o2); // 注册 o2
    s.notifyObservers("info1");  // 向观察者通知消息
    
    System.out.println("remove observer o1");
    
    s.removeObserver(o1);  // 移除 o1
    s.notifyObservers("info2"); // 再向观察者通知消息
    

    输出如下:

    o1 get info: info1
    o2 get info: info1
    remove observer o1
    o2 get info: info2
    

    可以看到,第一次通知消息时,o1o2 都收到消息了,在移除 o1 之后再发送消息,只有 o2 能收到消息。

    这就是观察者模式最简洁的一种实现方式,非常简单。我把完整的代码放在了这里

    4,观察者模式扩展

    根据不同的应用场景和需求,观察者模式可以有不同的实现方式,比如下面几种:

    • 同步阻塞的实现方式
    • 异步非阻塞的实现方式
    • 进程内的实现方式
    • 跨进程的实现方式

    同步阻塞方式

    根据这种划分方式,上面我们实现的就是同步阻塞的方式,当有新消息的时候,Subject 会将消息 notify 给所有的 Observer,直到所有的 Observer 执行完毕它的 update 过程,整个通知过程才算完毕,这整个过程是一个阻塞的过程。

    异步非阻塞方式

    为了加快整个 notify 过程,我们可以将同步阻塞的方式改为异步非阻塞的方式。

    一种简单的实现就是使用线程,就是在 update 方法中使用线程来完成任务,如下:

    public void update(String info) {
       Thread t = new Thread(new Runnable() {
           public void run() {
               // 处理任务
           }
       });
    
       t.start();
    }
    

    Google Guava EventBusExplained 是一个通用的观察者模式框架,你可以去了解一下。

    跨进程方式

    同步阻塞异步非阻塞都属于进程之内的实现,对于跨进程的实现,一般都是基于消息队列来实现。至于这方面的应用,有很多现成的,成熟的组件可以使用,比如:

    • Redis Pub/Sub:一个快速、稳定的发布/订阅消息传递系统。
    • ActiveMQ:一个基于 Java 的多协议的消息传递服务。
    • RocketMQ:一个消息传递引擎。
    • Kafka:一个分布式的大数据流处理平台。

    5,总结

    观察者模式旨在将观察者与被观察者解耦,在很多地方都用到了该模式,比如 SwingJavaBeans 等。

    观察者模式最经典的实现方式很简单,在实际应用中,可以在其基础上进行改进。

    (本节完。)


    推荐阅读:

    设计模式之高质量代码

    单例模式-让一个类只有一个对象

    工厂模式-将对象的创建封装起来

    策略模式-定义一个算法族


    欢迎关注作者公众号,获取更多技术干货。

    码农充电站pro

  • 相关阅读:
    域名和IP地址的关系通俗解释
    简单卷、跨区卷、带区卷、镜像卷和 RAID5的区别
    什么是网络端口
    Windows7 64 bit 下解决:检索 COM 类工厂中 CLSID 为 {0002450000000000C000000000000046} 的组件时失败
    SQL函数,收藏先。
    C#中抽象类和接口的区别(转)
    SQL数据库碎片检查DBCC SHOWCONTIG含义
    SQL锁表语句
    50种方法优化SQL Server
    简单工厂模式(转)
  • 原文地址:https://www.cnblogs.com/codeshell/p/14205145.html
Copyright © 2011-2022 走看看