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

    一、观察者模式概述

    定义:

    观察者模式(Observer Pattern):定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。别名有:发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式、从属者(Dependents)模式。

    4个角色:

    Subject(目标):被观察的对象。维护观察者集合(增删),定义通知观察者方法notify()。

    ConcreteSubject(具体目标):包含经常发生改变的数据,变化时,触发notify所有观察者。

    Observer(观察者):接口声明update()更新方法

    ConcreteObserver(具体观察者):维护一个指向具体目标对象的引用,存储关心的数据,当具体目标数据变化时触发update(),更新数据以保持和目标数据一致。

    二、JDK对观察者模式的支持

    如上图所示,由于观察者模式被多次使用, JDK源码就封装在rt.jar里面的java.util包下面,和顶级类Objects、各种java集合在一个级别,这种待遇惊呆了我。看来不好好剖析一下是不行了。

    1.Observable类

    充当观察目标类,用Vector集合来存储观察者(预留一张飞机票:Vector集合详解)。

    封装方法:增加、删除观察者、维护是否变化、通知观察者。(值得一提的是这些方法全部用synchronized修饰解决了并发脏数据问题)

    2.Observer接口

    充当抽象观察者,声明了一个update接口,当观察目标Observable的子类发生变化时,调用notifyObservers方法通知观察者,执行具体观察者实现类中的update方法。

    3.测试一下:

     1 package designPatterns.observerPattern;
     2 
     3 import java.util.Observable;
     4 import java.util.Observer;
     5 
     6 /**
     7  * 
     8  * @ClassName:MyObserver
     9  * @Description:观察者
    10  * @author denny.zhang
    11  * @date 2018年1月23日上午10:50:13
    12  */
    13 public class MyObserver implements Observer {
    14 
    15     private String name;
    16 
    17     public MyObserver(Observable o, String name) {
    18         o.addObserver(this);
    19         this.name = name;
    20     }
    21 
    22     @Override
    23     public void update(Observable o, Object arg) {
    24         System.out.println("观察者" + name + "触发更新!arg=" + arg + ",目标的观察者数量=" + o.countObservers());
    25     }
    26 
    27 }
     1 package designPatterns.observerPattern;
     2 
     3 import java.util.Observable;
     4 
     5 /**
     6  * 
     7  * @ClassName:MyObserverable
     8  * @Description:观察目标
     9  * @author denny.zhang
    10  * @date 2018年1月23日上午10:45:26
    11  */
    12 public class MyObserverable extends Observable {
    13     //观察目标初始数据
    14     private String data = "data0";
    15 
    16     public String getData() {
    17         return data;
    18     }
    19 
    20     public void setData(String data) {
    21         //如果数据变化了
    22         if (!this.data.equals(data)) {
    23             this.data = data;
    24             setChanged();//置变化状态为true
    25         }
    26         notifyObservers(data);//通知所有观察者
    27     }
    28 
    29 }
     1 package designPatterns.observerPattern;
     2 
     3 /**
     4  * 
     5  * @ClassName:MyTest
     6  * @Description:利用JDK自带观察者Observer接口和,目标Observable类,测试样例。
     7  * @author denny.zhang
     8  * @date 2018年1月23日下午12:49:53
     9  */
    10 public class MyTest {
    11 
    12     public static void main(String[] args) {
    13         //构造观察目标
    14         MyObserverable observerable = new MyObserverable();
    15         //构造2个观察者实现类:添加观察者进观察目标
    16         MyObserver observer1 = new MyObserver(observerable, "1");
    17         MyObserver observer2 = new MyObserver(observerable, "2");
    18         System.out.println("===1.目标初始值=" + observerable.getData());
    19         System.out.println("===2.给目标设置不同值[data1],看观察者是否触发更新!==========");
    20         //设置目标值
    21         observerable.setData("data1");
    22         System.out.println("===3.给目标设置想同值[data1],看观察者是否触发更新!==========");
    23         //设置目标相同值
    24         observerable.setData("data1");
    25     }
    26 }

    运行结果:

    ===1.目标初始值=data0
    ===2.给目标设置不同值[data1],看观察者是否触发更新!==========
    观察者2触发更新!arg=data1,目标的观察者数量=2
    观察者1触发更新!arg=data1,目标的观察者数量=2
    ===3.给目标设置想同值[data1],看观察者是否触发更新!==========

    看上面运行结果,初始data=data0,当观察者目标设置data1时,setChanged()触发Observable类变量changed=true,然后触发notifyObservers()通知所有的观察者,执行update方法。如下图:

     1 public void notifyObservers(Object arg) {
     6         Object[] arrLocal;
     7
     8         synchronized (this) {
    21             if (!changed)//如果没变化,直接返回
    22                 return;
    23             arrLocal = obs.toArray();//如果变化了,重新获取观察者数组
    24             clearChanged();//关闭变化
    25         }
    26         //遍历所有观察者,触发更新方法
    27         for (int i = arrLocal.length-1; i>=0; i--)
    28             ((Observer)arrLocal[i]).update(this, arg);
    29     }

    三、应用实例

    1.java事件驱动模型

    JDK1.1后的版本中,事件驱动模型采用基于观察者模式的委派事件模型(Delegation Event Model,DEM),即把事件委派给独立的事件处理对象(Listener)。

    分3个角色:

    1)事件发布者(事件源):Event Source,充当观察者模式的观察目标

    2)事件对象:Event Object

    3)事件监听器:Event Listener,充当观察者模式的观察者

    如下图所示,就是一个典型的DEM模型,Spring-context源码包,具体应用是在Spring-boot容器启动流程中触发各种6种定义好的事件。用户可以实现接口:ApplicationListener<ApplicationReadyEvent>(不一定是ApplicationReadyEvent,共定义了6种事件),然后复写onApplicationEvent方法,实现自己的业务逻辑。飞机票:spring boot容器启动详解

    2.MVC架构

    MVC(Model-View-Controller)架构是当前流行架构之一,也应用了观察者模式。角色如下:

    1.模型(Model):充当观察目标

    2.视图(View):充当观察者

    3.控制器(Controller)

    这里更多的是一种思想,暂时还没找到Spring MVC源码来支撑。

    四、总结

    1.优点

    1)把观察目标和观察者之间建立一个抽象的耦合, 观察目标只需要维护观察者集合即可,并不依赖于观察者。

    2)满足开闭原则,新增观察者无需修改观察目标代码。

    3)支持广播通信,观察目标会通知所有观察者。实现了1对多通信。

    2.缺点

    1)如果观察者过多,会影响观察目标的性能。因为需要通知所有观察者。

    2)观察模式不可滥用,过多导致程序逻辑复杂,分析问题难度增加。

    3.适用场景

    适合某些"交互定义良好"的场合使用,例如:

    1)一个对象的改变导致其它一个或者多个对象也发生改变,且并不知道有多少个对象需要发生改变。

    2)一个抽象模型有2个方面,其中一个方面依赖另一个方面,独立封装为2个对象,使它们各自独立的改变和复用。

  • 相关阅读:
    Qt之添加QLabel的点击事件
    Qt之布局管理--基本布局
    Qt之界面实现技巧
    Qt之键盘讲解
    Qt之多窗口切换
    Qt之自定义信号和槽函数
    Qt之重写QLabel类
    mysql学习(四)-字段类型
    mysql学习(三)
    mysql 复习与学习(二)数据库及表结构的创建删除
  • 原文地址:https://www.cnblogs.com/dennyzhangdd/p/8343229.html
Copyright © 2011-2022 走看看