• 设计模式之【观察者模式】


    表妹:哥啊,最近有个粉丝,老是动不动就喷。

    :是不是你写的文章不好呀?

    表妹:才不是,也收到很多粉丝的鼓励和点赞。

    :这种网络喷子把他从粉丝列表中移除就好啦。

    你看,这不就是我们设计模式中的观察者模式嘛?该模式又称为发布-订阅模式

    定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

    它和生产-消费模型有什么区别呢?

    不同点:

    观察者模式(发布-订阅模型),是一对多的关系,可以以同步的方式实现,也可以以异步的方式实现,订阅者之间没有竞争关系。

    生产-消费模型,是多对多的关系,一般以异步的方式实现,消费者之间存在竞争关系。

    共同点:

    两者都可以达到解耦的作用。

    观察者模式

     

    • Observable被观察者

      定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。

    • Observer观察者

      观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。

    • ConcreteObservable具体的被观察者

      定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

    • ConcreteObserver具体的观察者

      每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

    我们熟悉的公共号就是一种观察者模式。一个公众号,会有多个粉丝对象来关注,当公众号发布推文的时候,所有关注的粉丝都会收到公众号更新的通知,然后粉丝们会对通知做出相应的响应,执行相应的业务功能处理,比如阅读文章,点赞文章,收藏文章等。

    首先,定义观察者和被观察者两个接口,方便扩展。

    复制代码
    1 interface Observable {
    2     void addObserver(Observer observer);
    3     void removeObserver(Observer observer);
    4     void notifyObservers(String message);
    5 }
    6 interface Observer { 7 void update(String name, String message); 8 }
    复制代码

    ConcreteObservable:OfficialAccountOwner公众号主,具体的被观测者。

    复制代码
     1 class OfficialAccountOwner implements Observable {
     2     // 存储已注册的观察者,当事件发生时,通知列表中的所有观察者
     3     private List<Observer> fans;
     4     private String name;
     5     
     6     public OfficialAccountOwner(String name) {
     7         this.name = name;
     8         this.fans = new LinkedList<>();
     9     }
    10     
    11     // 发布文章
    12     public void sendMessage(String message) {
    13         this.notifyObservers(message);
    14     }
    15     
    16     @Override
    17     public void addObserver(Observer observer) {
    18         this.fans.add(observer);
    19     }
    20     
    21     @Override 
    22     public void removeObserver(Observer observer) {
    23         this.fans.remove(observer);
    24     }
    25     
    26     @Override
    27     public void notifyObservers(String message) {
    28         this.fans.forEach(fan-> {
    29             fan.update(this.name, message);
    30         });
    31     }
    32 }
    复制代码

    ConcreteObserver:具体的观察者,Fans1和Fans2。

    复制代码
     1 class Fans1 implements Observer {
     2     private String name;
     3     
     4     public Fans1(String name) {
     5         this.name = name;
     6     }
     7     
     8     @Override
     9     public void update(String name, String message) {
    10         System.out.println(this.name + "阅读" + name + "发的文章: " + message);
    11     }
    12 }
    13 ​
    14 class Fans2 implements Observer {
    15     private String name;
    16     
    17     public Fans2(String name) {
    18         this.name = name;
    19     }
    20     
    21     @Override
    22     public void update(String name, String message) {
    23         System.out.println(this.name + "一键三连了" + name + "发的文章。");
    24     }
    25 }
    复制代码

    接下来,我们来看一下实现效果。

    复制代码
     1 public class Demo {
     2     public static void main(String[] args) {
     3         OfficialAccountOwner DaWei = new OfficialAccountOwner("Gopher大威");
     4         Fans1 lisi = new Fans1("李四");
     5         Fans2 wangwu = new Fans2("王五");
     6         DaWei.addObserver(lisi);
     7         DaWei.addOvserver(wangwu);
     8         DaWei.sendMessage("设计模式之【观察者模式】");
     9         // 李四这个粉丝,老是无缘无故喷,移除他
    10         DaWei.removeObserver(lisi);
    11         DaWei.sendMessage("设计模式之【发布订阅模式】");
    12     }
    13 }
    14 ​
    15 // 打印结果:
    16 李四阅读Gopher大威发的文章:设计模式之【观察者模式】。
    17 王五一键三连了Gopher大威发的文章。
    18 // 粉丝李四被移除后
    19 王五一键三连了Gopher大威发的文章。
    复制代码

    你看,通过观察者模式,将观察者和被观察者代码解耦。如果观察者对象有新的更新逻辑的话,只需要添加一个实现了Observer接口的类,并通过addObserver()函数将它注册到观察者列表中即可,当被观察者有事件响应的时候,就会通知到列表中的所有观察者。

    观察者模式的优点

    • 观察者和被观察者之间是抽象耦合

      如此设计,则不管是增加观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手。

    • 建立一套触发机制

    观察者模式的缺点

    • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会很花时间。

    • 如果观察者和观察目标间有循环依赖,可能导致系统崩溃。

    • 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的。

    观察者模式的应用场景

    • 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。

    • 事件多级触发场景。

    • 跨系统的消息交换场景,如消息队列的处理机制。

    注意事项

    采用异步非阻塞方式,可以避免某一观察者错误导致系统卡壳。实际场景可选择消息队列,spring事件机制等。

    观察者模式在开源代码中的应用

    Spring中的事件驱动模型也叫发布订阅模式,是观察者模式的一个典型应用。

    事件机制的实现需要三个部分,事件源、事件、事件监听器。

    ApplicationEvent就相当于事件,ApplicationListener相当于事件监听器,ApplicaitonContext就是事件源。

    通过ApplicationEvent抽象类和ApplicationListener接口,可以实现ApplicationContext事件处理。监听器在处理Event时,通常会进行判断传入的Event是不是自己所要处理的,使用instanceof关键字。

    ApplicationEventMulticaster事件广播器实现了监听器的注册,一般不需要我们实现,只需要显示的调用applicationcontext.publisherEvent方法即可。

    感兴趣的同学,可以去看一下源码。

    总结

    观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子,比如,邮件订阅,RSS Feeds,本质上都是观察者模式。不同的应用场景和需求下,这个模式也有截然不同的实现方式,有同步阻塞的实现方式,也有异步非阻塞的实现方式;有进程内的实现方式,也有跨进程的实现方式。

    参考

    极客时间专栏《设计模式之美》

  • 相关阅读:
    力扣 1582. 二进制矩阵中的特殊位置
    Sharding JDBC案例实战
    合并两个非降数组
    容器卷挂载的秘密
    PLC维护有何难处?如何实现远程维护?
    SystemVerilog学习-07-类的继承和包的使用
    进程/线程绑定cpu方法探究
    寒冬已逝,“量子春天”正来
    Solaris 9 Sparc下安装整合Apache2和Tomcat5
    兆德心理平台系统模式开发介绍
  • 原文地址:https://www.cnblogs.com/Gopher-Wei/p/16099058.html