目录

一、定义

二、观察者模式案例分析

三、观察者模式在JDK中的应用

四、总结


一、定义

观察者模式,也叫发布订阅模式,它是一个在项目中经常使用的模式。其定义如下:

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。通过观察者模式,使得某个对象与其他依赖它的对象保持状态同步。(观察者订阅被观察者的状态,当被观察者状态改变的时候会通知所有订阅的观察者的过程)。

观察者模式的通用类图如下:

我们先来了解一下观察者模式的几个角色:

  • Subject被观察者:抽象被观察者角色,也叫被订阅对象,通常是接口或者抽象类。当被观察的状态发生变化时,需要通知所有观察者对象进行在状态更新。Subject通常具有注册、移除以及通知所有观察者对象的抽象方法;
  • ConcreteSubject具体的被观察者 : 具体的被观察者角色,实现了注册、移除、通知订阅者的方法;
  • Observer观察者:抽象观察者角色,可以是接口或抽象类,当Subject的状态发生变化时,Observer对象将会进行同步更新;
  • ConcreteObserver :具体观察者角色,接收到被观察者的回调之后会处理一系列业务逻辑;

二、观察者模式案例分析

我们以红绿灯的案例来说明观察者模式的使用方法:

司机朋友在看到红灯时,必须把车辆停下来;当绿灯时,才允许启动车辆。

结合前面的类图,我们可以发现红绿灯其实就是被观察者角色,司机朋友就是观察者【司机朋友观察红绿灯的状态,一旦红绿灯发生变化,司机朋友就会触发某种动作,如停车、启动车辆等】;

相关类图如下:

首先我们定义一下抽象的观察者角色:

/*** 抽象观察者角色*/
public abstract class AbstractObserver {/*** 被观察者状态变更时执行的方法*/protected abstract void update(String type);}

然后定义两个具体的观察者角色,即两个司机朋友,代码如下:

/*** 具体观察者角色*/
public class DriverA extends AbstractObserver {@Overrideprotected void update(String type) {if ("1".equals(type)) {System.out.println("司机A看到绿灯亮,开始启动汽车...");} else {System.out.println("司机A看到红灯亮,开始停止汽车...");}}
}/*** 具体观察者角色*/
public class DriverB extends AbstractObserver {@Overrideprotected void update(String type) {if ("1".equals(type)) {System.out.println("司机B看到绿灯亮,开始启动汽车...");} else {System.out.println("司机B看到红灯亮,开始停止汽车...");}}
}

有了观察者角色,下面定义抽象的被观察者角色:即红绿灯

/*** 抽象被观察者角色:红绿灯*/
public abstract class AbstractObservable {/*** 所有的观察者集合*/protected List<AbstractObserver> observerList = new ArrayList<>();/*** 添加观察者** @param observer*/public void addObserver(AbstractObserver observer) {this.observerList.add(observer);}/*** 删除观察者** @param observer*/public void deleteObserver(AbstractObserver observer) {this.observerList.remove(observer);}/*** 返回当前观察者的个数*/public int getObserverSize() {return this.observerList.size();}/*** 通知所有的观察者的抽象方法*/protected void notifyObserver() {for (AbstractObserver observer : observerList) {observer.update(type());}}/*** 类型*/protected abstract String type();}

可以看到,抽象被观察者内部持有一个存放具体观察者角色的容器,并且提供了添加、删除观察者的方法。

然后定义具体的被观察者角色:即红灯、绿灯:

/*** 具体被观察者: 绿灯*/
public class GreenLight extends AbstractObservable {@Overrideprotected void notifyObserver() {System.out.println("绿灯亮了...");super.notifyObserver();}@Overrideprotected String type() {return "1";}
}/*** 具体被观察者: 红灯*/
public class RedLight extends AbstractObservable {@Overrideprotected void notifyObserver() {System.out.println("红灯亮了...");super.notifyObserver();}@Overrideprotected String type() {return "2";}
}

最后我们写一个场景类,看下如何使用观察者模式实现订阅发布通知:

/*** 场景类*/
public class Client {public static void main(String[] args) {AbstractObservable observable = new RedLight();DriverA driverA = new DriverA();DriverB driverB = new DriverB();observable.addObserver(driverA);observable.addObserver(driverB);observable.notifyObserver();System.out.println("观察红绿灯的司机共有: " + observable.getObserverSize() + "位");System.out.println("===========================");observable = new GreenLight();observable.addObserver(driverA);observable.notifyObserver();System.out.println("观察红绿灯的司机共有: " + observable.getObserverSize() + "位");}
}

程序运行结果如下:

红灯亮了...
司机A看到红灯亮,开始停止汽车...
司机B看到红灯亮,开始停止汽车...
观察红绿灯的司机共有: 2位
===========================
绿灯亮了...
司机A看到绿灯亮,开始启动汽车...
观察红绿灯的司机共有: 1位

可以看到,当红绿灯状态发生变更时,能够触发所有观察者执行某个动作。并且使用了观察者角色,如果想动态增加订阅者就显得非常方便,只需要增加一个具体的观察者对象即可。

三、观察者模式在JDK中的应用

JDK中的Observable接口就使用到了观察者模式,一个 Observable 实例改变后,调用 Observable 的 notifyObservers() 方法的应用程序会通过调用观察者的 update() 方法来通知观察者该实例发生了改变。

【a】Observable:具体的被观察者角色,此处并没有抽象被观察者角色。

public class Observable {private boolean changed = false;//持有观察者角色的引用对象集合private Vector<Observer> obs;public Observable() {obs = new Vector<>();}//注册观察者public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}
}//移除观察者
public synchronized void deleteObserver(Observer o) {obs.removeElement(o);
}//通知所有观察者
public void notifyObservers() {notifyObservers(null);
}public void notifyObservers(Object arg) {Object[] arrLocal;synchronized (this) {if (!changed)return;arrLocal = obs.toArray();clearChanged();}//循环遍历,通知所有观察者,并且调用update()更新状态for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);
}//...省略}

【b】抽象观察者角色:Observer

public interface Observer {//更新状态(只要改变了 observable 对象就调用此方法)void update(Observable o, Object arg);
}

四、总结

观察者模式的优点:

  • 动态新增一个观察者非常方便,并且不需要修改被观察者代码,可维护性高,扩展性强,遵循开闭原则;
  • 观察者和被观察者之间是抽象耦合的,修改其中一方并不会影响另一方;

观察者模式的缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
  • 需要考虑一下开发效率和运行效率的问题,一个被观察者,多个观察者,开发和调试会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的运行效率。在这种情况下,一般考虑采用异步的方式。

观察者模式的使用场景:

  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象,可以使用观察者模式创建一种链式触发机制;
  • 一个对象必须通知其他对象,而并不知道这些对象是谁;
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度;

观察者模式和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递的过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。

设计模式 (十八 ) 观察者模式相关推荐

  1. 设计模式 ( 十六 ) 观察者模式Observer(对象行为型)

    设计模式 ( 十五 ) 观察者模式Observer(对象行为型) 1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来 ...

  2. 设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

    设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕 ...

  3. Java设计模式(十八):享元设计模式

    1. 应用场景 当我们项目中创建很多对象,而且这些对象存在许多相同模块,这时,我们可以将这些相同的模块提取出来采用享元模式生成单一对象,再使用这个对象与之前的诸多对象进行配合使用,这样无疑会节省很多空 ...

  4. 设计模式十八:mediator(中介者模式)——对象行为型模式

    mediator(中介者模式)--对象行为型模式 1.意图 用一个中介对象来封装一系列的对象交互.中介者使不同对象不需要显式地相互引用,从而使其松散耦合,从而使得可以独立地改变他们之间的交互. 2.动 ...

  5. 【每天一个java设计模式(十八)】 - 观察者模式

    观察者模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.这种模式有时又称作发布-订阅模式.模型-视图模式,它是对象行为型模式. 观察者 ...

  6. C#设计模式之十六观察者模式(Observer Pattern)【行为型】

    C#设计模式之十六观察者模式(Observer Pattern)[行为型] 原文:C#设计模式之十六观察者模式(Observer Pattern)[行为型] 一.引言 今天是2017年11月份的最后一 ...

  7. 十八、职责链模式-推卸责任,不关我的事,我不管!#和设计模式一起旅行#

    不在其位,不谋其政! –出自<论语·泰伯> 故事背景 在现实世界中,有很多情况下会遇到一些推卸责任的场景,比如要办理一件事的时候,被告诉你要去做个做这个事情,但是去了这个地方,确告诉要到另 ...

  8. 燕十八PHP高性能架构班教学视频教程

    彻底拿下Linux集群+Nginx高并发+lvs负载均衡+MySQL优化+NoSQL! 2000W万条真实网站数据,拒绝纸上谈兵! Linux资深运维 + 燕十八老师亲自授课! Linux优化篇1.m ...

  9. (十八)享元模式详解(都市异能版) - 转

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 魔都. 自从越狱风波过去以后,小左的生活便又回到了之前的节奏,依旧是每日徘徊在魔都某天桥,继续着自己的算命之旅. 说起这次越狱风波,着 ...

  10. 想要成为一名优秀的程序员,这十八招必看

    文章目录 第一招:速学能力 第二招:理解能力 第三招:编程能力 第四招:使用能力 第五招:编码能力 第六招:注释能力 第七招:思维能力 第八招:英语能力 第九招:学习能力 第十招:思考能力 第十一招: ...

最新文章

  1. WebAPI 2.x中如何扩展Identity Store
  2. 【大神】软件建模仿真空气冷凝集水器,末日饮水荒野求生水资源解决方案
  3. 百度地图手机和电脑不一致_如何解决电脑显色和印刷色不一致的问题
  4. [原]Console小技巧——Console版贪食蛇
  5. regester正则用法_Regester学习笔记
  6. 【编译原理笔记06】语法分析,移入-归约分析:自底向上的分析,LR(0)分析法,LR(0)分析表的构建(基于自动机)
  7. Android消息驱动Handler类分析
  8. python求一个数的因子_python语言求因数方法的实现源码
  9. PLC控制系统设计的基本原则和主要内容
  10. 计算机英语CMYK全称,CMYK是什么意思 CMYK与RGB的区别介绍
  11. [CA-1]存储器(Caches)
  12. 研报复现系列(六)【国泰君安】基于CCK模型的股票市场羊群效应研究
  13. 测试开发进阶——常用中间件概念——线程与线程池理解
  14. Python 错误 RuntimeError: CUDA error (10): invalid device ordinal
  15. 2023中国眼博会/护眼仪/CEYEE青少年眼健康产业展览会
  16. Python常用开发软件有哪些?
  17. linux自动补全命令插件,vim自动补全插件snipMate
  18. TS 36.211 V12.0.0-上行(1)-时隙结构和物理资源
  19. Java 实现回文数
  20. 比价寄快递CPS小程序开发

热门文章

  1. 图像空域增强:直方图方法
  2. 图像特征的匹配-OpenCV3.0
  3. git add/rm/mv文件到暂存区
  4. 381.O(1)时间插入、删除和获取随机元素-允许重复
  5. android camera调试打印信息,Android : 高通平台Camera调试
  6. 凸优化第三章凸函数 3.6关于广义不等式的凸性
  7. Ubuntu sudo nopasswd方法
  8. 降维系列之 LE 拉普拉斯特征映射
  9. 逆向动态调试之Ollydbg的使用
  10. Hvv近期0day总结三