当前流行的 MVC(Model/View/Controller,模型/视图/控制器)架构中也应用了观察者模式,如下图所示。

模型层Model提供的数据是视图层View所观察的对象,在视图层中包含了两个数据显示图表对象,一个是柱状图,一个是饼状图,同样的数据可能有不同的图表显示方式,如果模型层的数据发生改变,则两个图表对象将跟随着发生改变。

这意味着图表对象依赖模型层提供的数据对象,因此数据对象的任何状态改变都应立即通知它们。但是这两个图表之间相互独立,不存在任何联系,而且图表对象的个数没有任何限制,用户可以根据需要再增加新的图表对象,如折线图。也就是说,相同的数据可以对应任意多个图表对象,而且还可以根据需要增加新的图表对象,增加新的图表对象时,对原有系统几乎没有任何影响,满足开闭原则的要求。

建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。


观察者模式(Observer Pattern)文章目录

  • 模式介绍
  • 模式结构
  • 模式应用场景
  • 练习案例:猫,狗与老鼠
  • 总结
  • 模式扩展(Java语言提供的对观察者模式的支持)

模式介绍

观察者模式主要解决一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作,通过上面的例子我们对该模式已经有了一定的认知,如果要一句话来解释观察者模式呢,大概就是:

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

观察者模式又叫做发布-订阅(Publish/Subscribe)模式,模型-视图( Model/View)模式、源-监听器( Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。


模式结构

观察者模式结构较为复杂,它包括观察目标观察者两个层次结构,

  1. Subject(目标)

目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,它可以存储任意数量的观察者对象,它提供一个接口来增加和删除观察者对象,同时它定义了的通知方法notifyO。目标类可以是接口,也可以是抽象类或实现类。

  1. ConcreteSubject(具体目标)

具体目标是目标类的子类,通常它包含经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知。同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。

  1. Observer(观察者)

观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。

  1. ConcreteObserver(具体观察者)

在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法。通常在实现时,可以调用具体目标类的attach()方法将自己添加到目标类的观察者集合中或通过detach()方法将自己从目标类的观察者集合中删除。


模式应用场景

观察者模式在软件开发中应用非常广泛,如某电子商务网站可以在执行发送操作时给多个用户发送商品打折信息,某团队战斗游戏中队友牺牲将给所有成员提示等,凡是涉及一对一或者一对多的对象交互场景都可以使用观察者模式

例如:JDK 1.0及更早的AWT事件模型基于职责链模式,但是这种模型不适用于复杂的系统,因此在JDK 1.1版本及以后的各个版本中,事件处理模型采用基于观察者模式的委派事件模型(Delegation Event Model,DEM)。

在DEM模型里面,目标角色(Subject)负责发布事件,而观察者角色(Observer)可以向目标订阅它所感兴趣的事件。当一个具体目标产生一个事件时,它将通知所有订阅者。在DEM 中 ,事件的发布者称为事件源(Event Source),而订阅者叫做事件监听器(EventListener),在这个过程中还可以通过事件对象(Event Object)来传递与事件相关的信息。可以在事件监听者的实现类中实现事件处理,因此事件监听对象又可以称为事件处理对象。事件源对象、事件监听对象(事件处理对象)和事件对象构成了Java事件处理模型的三要素。


练习案例:猫,狗与老鼠

假设猫是老鼠和狗的观察目标,老鼠和狗是观察者,猫叫老鼠跑,狗也跟着叫,使用观察者模式描述该过程。

  1. 抽象目标类 MySubject

MySubject是抽象目标类,在其中定义了一个ArrayList类型的集合observers,用于存储观察者对象,并定义了注册方法attach()和注销方法detach() ,同时声明了抽象的通知方法 cry()。需要注意的是attach()方法和detach()方法都必须针对抽象观察者进行编程,任何抽象观察者的子类对象都可以注册或注销。

package observer.test1;import java.util.ArrayList;/*** @author mengzhichao* @create 2021-12-04-12:17*/
public abstract class MySubject {protected ArrayList observers =new ArrayList();//注册方法public void attach(MyObserver observer){observers.add(observer);}//注销方法public void detach(MyObserver observer){observers.remove(observer);}public abstract void cry(); //抽象通知方法}
  1. 抽象观察者类 MyObserver

抽象观察者MyObserver定义为一个接口,在其中声明了抽象响应方法response()

package observer.test1;/*** @author mengzhichao* @create 2021-12-04-12:20*/
public interface MyObserver {void response(); //抽象响应方法
}
  1. 具体目标类 Cat(猫类)

Cat是目标类MySubject 的子类,它实现了抽象方法 cry() ,在cry()中遍历了观察者集合,调用每一个观察者对象的response()响应方法。

package observer.test1;/*** @author mengzhichao* @create 2021-12-04-12:19*/
public class Cat extends MySubject {@Overridepublic void cry() {System.out.println("猫叫!");System.out.println("-----------------------------------------");for (Object obs:observers){((MyObserver)obs).response();}}
}
  1. 具体观察者类 Mouse(老鼠类)

Mouse是具体观察者类,它实现了在抽象观察者中定义的响应方法response()

package observer.test1;/*** @author mengzhichao* @create 2021-12-04-13:04*/
public class Mouse implements MyObserver {@Overridepublic void response() {System.out.println("老鼠努力逃跑!");}
}
  1. 具体观察者类 Dog(狗类)

Dog也是具体观察者类,它实现了在抽象观察者中定义的响应方法response()

package observer.test1;/*** @author mengzhichao* @create 2021-12-04-13:05*/
public class Dog implements MyObserver {@Overridepublic void response() {System.out.println("狗跟着叫!");}
}
  1. 编写客户端类并测试

在客户端代码中需要实例化具体目标类和具体观察者类,先调用目标对象的attach()方法来注册观察者,再调用目标对象的cry()方法,在cry()方法的内部将调用观察者对象的响应方法。

package observer.test1;/*** @author mengzhichao* @create 2021-12-04-13:05*/
public class Client {public static void main(String[] args) {MySubject subject=new Cat();MyObserver obs1,obs2,obs3;obs1=new Mouse();obs2=new Mouse();obs3=new Dog();subject.attach(obs1);subject.attach(obs2);subject.attach(obs3);subject.cry();}
}


观察者模式很好地体现了面向对象设计原则中的开闭原则

如果需要增加一个观察者,如猪也作为猫的观察者,但是猫叫猪无须有任何反应,只需要增加一个新的具体观察者类Pig,而对原有的类库无须做任何改动,这对于系统的扩展性和灵活性有很大提高。

新增的具体观察者类Pig的代码如下:

package observer.test1;/*** @author mengzhichao* @create 2021-12-04-13:13*/
public class Pig implements MyObserver {@Overridepublic void response() {System.out.println("猪没有反应!");}
}

在客户端代码中可以定义一个Pig实例,再将它注册到目标对象的观察者集合中,则需要增加如下代码:

package observer.test1;/*** @author mengzhichao* @create 2021-12-04-13:05*/
public class Client {public static void main(String[] args) {MySubject subject=new Cat();MyObserver obs1,obs2,obs3,obs4;obs1=new Mouse();obs2=new Mouse();obs3=new Dog();obs4=new Pig();subject.attach(obs1);subject.attach(obs2);subject.attach(obs3);subject.attach(obs4);subject.cry();}
}

从本实例可以看出增加新的具体观察者很容易,原有类库代码无须进行任何修改。 在实际使用时,还需要注意以下几个问题

  1. 在客户端尽量针对抽象目标和抽象观察者编程,可以将具体观察者类的类名存储在配置文件中,如果需要更换或增加具体观察者对象只需要修改配置文件即可。如果目标对象和观察者对象之间是一对一关系,则实现过程比较简单,但是如果目标和观察者是一对多关系,则实现过程相对较为复杂。
  2. 在本实例中,由于具体观察者与具体目标类之间没有关联关系,因此增加新的具体目标类也非常方便,只需要扩展抽象目标类即可,而且也可以通过配置文件来存储具体目标类的类名,提高系统的灵活性和可扩展性。
  3. 如果具体观察者与具体目标类之间存在关联关系,则增加新的具体目标类会比较复杂,如果原有观察者类需要访问新增加的具体目标类中的状态,需要修改原有观察者类的源代码,系统的扩展性受到一定影响,不符合“开闭原则”的要求。

总结

观察者模式定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

观察者模式适用情况包括:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面;一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变;一个对象必须通知其他对象,而并不知道这些对象是谁;需要在系统中创建一个触发链。

观察者模式包含四个角色:

  • 目标又称为主题,它是指被观察的对象﹔具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知﹔观察者将对观察目标的改变做出反应﹔在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。

观察者模式的主要优点在于可以实现表示层和数据逻辑层的分离,并在观察目标和观察者之间建立一个抽象的耦合,支持广播通信;其主要缺点在于如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间,而且如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。


模式扩展(Java语言提供的对观察者模式的支持)

  • 观察者模式在Java语言中的地位非常重要。在JDK的 java.util包中,提供Observable类以及Observer接口,它们构成了Java语言对观察者模式的支持。

  1. Observer接口

java.util. Observer接口只定义一个方法,它充当抽象观察者,其方法定义代码如下

void update(Observable o,object arg);

当观察目标的状态发生变化时,该方法将会被调用,在Observer的实现子类中实现该update()方法,即具体观察者可以根据需要具有不同的更新行为。当调用观察目标类Observable 的 notifyObservers()方法时,将调用观察者类中的update()方法。

  1. Observable类

java.util. Observable类充当观察目标类,在Observable中定义了一个向量Vector来存储观察者对象。它的方法包括:

方法名 作用
Observable() 构造函数,实例化Vector向量。
addObserver(Observer o) 用于注册新的观察者对象到向量中。
deleteObserver (Observer o) 用于删除向量中的某一个观察者对象。
notifyObservers( ) , notifyObservers(Object arg) :通知方法,在方法内部循环调月向量中每一个观察者的update()方法
deleteObservers 该方法用于清空向量,即删除向量中所有观察者对象
setChanged() 该方法被调用后会将一个boolean类型的内部标记变量changed i值设置为true,表示观察目标对象的状态发生了变化。
clearChanged() 该方法用于将changed变量的值设为false,表示对象状态不再发生改变或者已经通知了所有的观察者对象﹐调用了它们的update()方法。
hasChanged() 该方法用于测试对象状态是否改变。
countObservers() 该方法用于返回向量中观察者的数量。

我们可以直接使用Observer接口Observable类来作为观察者模式的抽象层,自定义具体的观察者类和观察目标类,通过使用Java API中的Observer接口和Observable类,可以更加方便地在Java语言中使用观察者模式。


好了,文章到此就结束了,如果觉得对你有帮助还请一键三联支持一下!

详解Java设计模式之观察者模式(Observer Pattern)相关推荐

  1. 设计模式学习--观察者模式(Observer Pattern)

    设计模式学习--观察者模式(Oberser Pattern) 2013年5月18日 天气:热!  下午15:28  设计模式学习中 学习者:小巫 什么是观察者模式? 定义了对象之间的一对多依赖,这样一 ...

  2. 锈才学设计模式之 —— 观察者模式(Observer Pattern)

    锈才学设计模式之  -- 观察者模式 观察者模式:定义对象的一对多的关系,这样当主题对象改变状态时,其它的观察者对象都会收到通知,自动更新. 说明: 在真实世界中存在很多类似的模型,比如:订报纸,找中 ...

  3. Java里氏转换_详解Java设计模式编程中的里氏替换原则

    定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 ...

  4. 设计模式 - 观察者模式(Observer Pattern) Java内置 用法

    观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...

  5. Java设计模式之观察者模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  6. 设计模式-观察者模式(Observer Pattern)

    设计模式-观察者模式 观察者模式是使用频率最高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应.在观察者模式中,发生改变的对象称为观 ...

  7. decorator java_java_详解java装饰模式(Decorator Pattern),一、装饰器模式(Decorator Patter - phpStudy...

    详解java装饰模式(Decorator Pattern) 一.装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式 ...

  8. 设计模式:观察者模式--Observer

    一.什么是观察者模式 1.生活中的观察者模式 1.警察抓小偷 在现实生活中,警察抓小偷是一个典型的观察者模式「这以一个惯犯在街道逛街然后被抓为例子」,这里小偷就是被观察者,各个干警就是观察者,干警时时 ...

  9. java 观察者模式_图解Java设计模式之观察者模式

    图解Java设计模式之观察者模式 天气预报项目需求 天气预报设计方案 1 - 普通方案 观察者模式(Observer)原理 观察者模式解决天气预报需求 观察者模式在JDK应用的源码分析 天气预报项目需 ...

  10. 详解Java中的正则表达式

    详解Java中的正则表达式,并列出常用的正则表达式语法和一些常用的场景. 判断一个字符串是否是由数字组成: 当不使用正则表达式的时候的实现代码: public class RegexDemo01 {p ...

最新文章

  1. 100年前伦敦爆发的霍乱,教会了人类什么?
  2. Floyd算法实验报告
  3. 寿命能推算吗?加州大学科学家提出“预测方法”
  4. CSS学习16之层级
  5. 彩票假设 (Lottery Ticket Hypothesis) 在CV、NLP和OOD领域的应用
  6. js 自定义DOM事件
  7. config设置源 使用pip_conda和pip重新配置源
  8. 数据库-创建数据库-创建数据表
  9. Retinex算法的C++/opencv实现
  10. Statistics Pro for Mac(统计学软件)
  11. linux node安装菜鸟教程,手把手告诉你如何安装多个版本的node
  12. python无限循环怎么结束,python的无限循环及退出
  13. 计算机辅助设计 Photoshop 教案,计算机辅助设计(photoshop)
  14. win10 启动自动修复失败
  15. Trac 经验谈之(5)插件篇
  16. 匹马抢三关:讯飞翻译机3.0的破障之战
  17. Git详解之六 Git工具
  18. 伺服的基础知识及简单应用
  19. 区块链技术研究综述:原理、进展与应用
  20. Openwrt实现双4G模块拨号

热门文章

  1. 利用ansys计算机械结构最小安全系数教程,利用ANSYS计算复杂薄壁杆件截面特性.pdf...
  2. MarkDown下载和安装
  3. qq红包领取支付宝红包功能,qqxml跳转技术
  4. [ PyQt入门教程 ] Qt Designer工具的使用
  5. Windows 软件管理
  6. 计算机c语言试题文档,计算机二级C语言考试试题
  7. python画圆形螺旋线_在PDMS中使用python直接生成管口方位图(开源分享第三集)...
  8. 微信小程序直播如何开通
  9. 如何制定软件开发计划
  10. 不用 PLC与变频器通信程序 不用 三菱PLC与变频器通讯程序不经过 PLC与变频器通信