• 设计模式-观察者模式


    前言

            整个9月份基本是在花式加班中疲劳度过的,工作中进步不少,自学进度却放慢了。十一长假,处理完家里的事情后提前一天来了上海,收拢一下思绪,准备迎接下一阶段的工作、学习。不知不觉,2019年只剩下了不到三个月,来自时间的压迫感无时无刻不在,需要抓紧最后的机会,利用好这三四个月的时间。

            闲话少叙,这一次我打算将观察者设计模式梳理一下,从JDK中的设计,到Spring中的应用,都会涉及到。心得以及感悟都是一家之言,如有不恰当之处,还望各位道友指正!

    一、结合案例分析java中的观察者模式

            首先看一下java中已经定义好了的观察者类(Observer)、被观察者类(Observable)的结构:

    1 // 观察者类
    2 public interface Observer {
    3     // 此方法用于定义观察者观察到变化后发生的行为
    4     // 第一个参数是被观察者;第二个参数是一个可变对象,方便动态传递某些信息
    5     void update(Observable o, Object arg);
    6 }
     1 public class Observable {
     2     // 变动标识,用于判断被观察者是否有变化
     3     private boolean changed = false;
     4     // 存放观察者
     5     private Vector<Observer> obs;
     6 
     7     public Observable() {
     8         obs = new Vector<>();
     9     }
    10   
    11     public synchronized void addObserver(Observer o) {
    12         if (o == null)
    13             throw new NullPointerException();
    14         if (!obs.contains(o)) {
    15             obs.addElement(o);
    16         }
    17     }
    18 
    19     public synchronized void deleteObserver(Observer o) {
    20         obs.removeElement(o);
    21     }
    22 
    23     public void notifyObservers() {
    24         notifyObservers(null);
    25     }
    26     // 通知被观察者发生了变化,循环调用update方法 ☆
    27     public void notifyObservers(Object arg) {
    28         Object[] arrLocal;
    29 
    30         synchronized (this) {
    31             if (!changed)
    32                 return;
    33             arrLocal = obs.toArray();
    34             clearChanged();
    35         }
    36 
    37         for (int i = arrLocal.length-1; i>=0; i--)
    38             ((Observer)arrLocal[i]).update(this, arg);
    39     }
    40 
    41     public synchronized void deleteObservers() {
    42         obs.removeAllElements();
    43     }
    44     // 将改变状态设置成已改变
    45     protected synchronized void setChanged() {
    46         changed = true;
    47     }
    48 
    49     protected synchronized void clearChanged() {
    50         changed = false;
    51     }
    52 
    53     public synchronized boolean hasChanged() {
    54         return changed;
    55     }
    56 
    57     public synchronized int countObservers() {
    58         return obs.size();
    59     }
    60 }

            可见被观察者类中维护了一个观察者的列表,在发生变化通知观察者时是循环列表,调用每个观察者的update方法,具体每个观察者是如何做的,取决于其update方法。

            知道了java中的观察者和被观察者类,我们要如何使用呢?且看下面的例子。

            十一回来的路上看了好几篇半佛仙人的文章,略有感悟,于是此次就用仙人的案例作为主体进行设计。仙人是一个风控出身的"社会工程学专家",热衷于写文章揭露社会上的沙雕事件、违法事件。所以此处观察者就是仙人,而被观察者则是违法事件,下面是定义出来的两个类:

     1 /**
     2  * 仙人是观察者,实现Observer观察者接口
     3  */
     4 public class CelestialBeing implements Observer {
     5 
     6     @Override
     7     public void update(Observable o, Object arg) {
     8         writeToPen();
     9     }
    10     // 写文章揭露(也就是喷)
    11     private void writeToPen(){
    12         System.out.println("写文章批斗、吊打");
    13     }
    14 }
     1 /**
     2  * 违法事件是被观察者,一旦出现变化,会被半佛仙人观察到
     3  */
     4 public class IllegalThing extends Observable {
     5 
     6     public void change() {
     7         System.out.println("出现了违法事件");
     8         super.setChanged();
     9     }
    10 }

    下面是测试类:

     1 public class TestClient {
     2 
     3     public static void main(String[] args) {
     4         IllegalThing illegalThing = new IllegalThing();
     5         /**
     6          * 观察者模式的关键点:被观察者持有观察者
     7          */
     8         illegalThing.addObserver(new CelestialBeing());
     9         // 有改变
    10         illegalThing.change();
    11         // 通知所有观察者
    12         illegalThing.notifyObservers();
    13 
    14     }
    15 }

    运行结果:

    1 出现了违法事件
    2 写文章批斗、吊打

            可以看到,由于被观察者类Observable自身已经维护好了观察者列表,所以我们的被观察者类不需要做太多的事情,只需要将setChanged方法暴露出去即可。观察者模式的关键,就是上面注释中提到的:被观察者持有观察者列表。只要注意了这一点,相信大家在实际场景中使用时便不会用错。

    二、看看观察者模式在Spring中的应用

    1、在Spring容器的某个阶段触发事件

            如果我想在Spring容器refresh之后触发某些特定逻辑,那么定义一个这样的类就可以:

    1 @Component
    2 public class MySpringListener implements ApplicationListener<ContextRefreshedEvent> {
    3 
    4     @Override
    5     public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
    6         System.out.println("MySpringObserver执行了");
    7         // 执行自定义逻辑
    8     }
    9 }

            能如此方便的做到,主要是因为Spring帮我们创建好了refresh的事件,在Spring容器过程中类似的事件有以下几个:

     2、自定义的事件触发

            如果我们想在某些特定的时机触发一个自定义的事件,比如在发邮件时触发一个事件监听,那么在Spring中要怎么做呢?

    首先定义一个邮件事件:

    1 public class MySpringEmailEvent extends ApplicationEvent {
    2     
    3     public MySpringEmailEvent(Object source) {
    4         super(source);
    5     }
    6 }
    1 @Component
    2 public class MySpringEmailListener implements ApplicationListener<MySpringEmailEvent> {
    3 
    4     @Override
    5     public void onApplicationEvent(MySpringEmailEvent mySpringEmailEvent) {
    6         System.out.println("MySpringEmailListener执行了");
    7         // 执行自定义逻辑
    8     }
    9 }
    1 @Component
    2 public class MyEventTrigger {
    3     @Autowired
    4     private ApplicationContext applicationContext;
    5     // 触发事件
    6     public void sendEmail () {
    7         applicationContext.publishEvent(new MySpringEmailEvent(applicationContext)); 
    8 }
    9 }

    测试类:

    1 public class SpringClient {
    2     public static void main(String[] args) {
    3         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    4         MyEventTrigger bean = applicationContext.getBean(MyEventTrigger.class);
    5         bean.sendEmail();
    6     }
    7 }

    执行结果:

    1 MySpringEmailListener执行了

            看到这里,相信很多道友会有疑问,Spring是如何将事件和listener关联在一起的呢?其实内部是通过ApplicationEventMulticaster接口,它是如何实现的呢?且看下回分析...

  • 相关阅读:
    FlowNet2.0论文笔记
    LeetCode NO477.汉明距离总和
    自然语言的分词方法之N-gram语言模型
    C++函数模板及其实例化和具体化
    Vue2源码解读(5)
    Vue2源码解读(4)
    Vue2源码解读(3)
    Vue2源码解读(2)
    Vue2源码解读(1)
    vue的双向绑定原理及实现
  • 原文地址:https://www.cnblogs.com/zzq6032010/p/11605335.html
Copyright © 2020-2023  润新知