java与设计模式-观察者模式

一、定义

观察者模式(Observer Pattern) 也叫做发布订阅模式(Publish/subscribe) ,它是一个在项
目中经常使用的模式, 其定义如下:Define a one-to-many dependency between objects so that when one object changes state,all itsdependents are notified and updated automatically.(定义对象间一种一对多的依赖关系, 使得每当一个对象改变状态, 则所有依赖于它的对象都会得到通知并被自动更新。 )

二、通用类图

三、角色分析

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

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

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

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

四、经典代码实现

Observer观察者

观察者一般是一个接口, 每一个实现该接口的实现类都是具体观察者。

public interface Observer {/*** 更新方法*/void update();
}

Observer具体观察者1

public class ConcreteObserver01 implements Observer {@Overridepublic void update() {System.out.println("间谍-01:观察到被观察者有变化,通知上级...");}
}

Observer具体观察者2

public class ConcreteObserver02 implements Observer {@Overridepublic void update() {System.out.println("间谍-02:观察到被观察者有变化,通知上级...");}
}

被观察者

被观察者的职责非常简单, 就是定义谁能够观察, 谁不能观察, 程序中使用ArrayList和Vector没有太大的差别, ArrayList是线程异步, 不安全; Vector是线程同步, 安全——就这点区别。 我们再来看具体的被观察者。

public abstract class Subject {/*** 观察者列表*/private Vector<Observer> observers = new Vector<>();/*** 添加观察者** @param observer 观察者*/public void addObserver(Observer observer) {this.observers.add(observer);}/*** 删除观察者** @param observer 观察者*/public void removeObserver(Observer observer) {this.observers.remove(observer);}/*** 通知所有观察者**/public void notifyObserver() {observers.forEach(Observer::update);}}

具体被观察者

public class ConcreteSubject extends Subject {public void doSomething() {System.out.println("具体被观察者: 我进行了一些计算活动...");super.notifyObserver();}
}

场景类

public class Main {public static void main(String[] args) {ConcreteSubject cs = new ConcreteSubject();ConcreteObserver01 co1 = new ConcreteObserver01();ConcreteObserver02 co2 = new ConcreteObserver02();cs.addObserver(co1);cs.addObserver(co2);cs.doSomething();}
}

运行结果:

具体被观察者: 我进行了一些计算活动...
间谍-01:观察到被观察者有变化,通知上级...
间谍-02:观察到被观察者有变化,通知上级...

五、观察者模式的应用与理解

5.1 观察者模式的优点

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

  • 建立一套触发机制
    根据单一职责原则, 每个类的职责是单一的, 那么怎么把各个单一的职责串联成真实世界的复杂的逻辑关系呢? 比如, 我们去打猎, 打死了一只母鹿, 母鹿有三个幼崽, 因失去了母鹿而饿死, 尸体又被两只秃鹰争抢, 因分配不均, 秃鹰开始斗殴, 然后羸弱的秃鹰死掉,生存下来的秃鹰, 则因此扩大了地盘……这就是一个触发机制, 形成了一个触发链。 观察者模式可以完美地实现这里的链条形式。

5.2 观察者模式的缺点

观察者模式需要考虑一下开发效率和运行效率问题, 一个被观察者, 多个观察者, 开发和调试就会比较复杂, 而且在Java中消息的通知默认是顺序执行, 一个观察者卡壳, 会影响整体的执行效率。 在这种情况下, 一般考虑采用异步的方式。

多级触发时的效率更是让人担忧, 大家在设计时注意考虑。

5.3 观察者模式的使用场景

  • 关联行为场景。 需要注意的是, 关联行为是可拆分的, 而不是“组合”关系。
  • 事件多级触发场景。
  • 跨系统的消息交换场景, 如消息队列的处理机制

5.3 观察者模式的注意事项

使用观察者模式也有以下两个重点问题要解决.

  • 广播链的问题

如果你做过数据库的触发器, 你就应该知道有一个触发器链的问题, 比如表A上写了一个触发器, 内容是一个字段更新后更新表B的一条数据, 而表B上也有个触发器, 要更新表C, 表C也有触发器……完蛋了, 这个数据库基本上就毁掉了! 我们的观察者模式也是一样的问题, 一个观察者可以有双重身份, 既是观察者, 也是被观察者, 这没什么问题呀, 但是链一旦建立, 这个逻辑就比较复杂, 可维护性非常差, 根据经验建议, 在一个观察者模式中最多出现一个对象既是观察者也是被观察者, 也就是说消息最多转发一次(传递两次) , 这还是比较好控制的。

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

  • 异步处理问题

这个EJB是一个非常好的例子, 被观察者发生动作了, 观察者要做出回应, 如果观察者比较多, 而且处理时间比较长怎么办? 那就用异步呗, 异步处理就要考虑线程安全和队列的问题, 这个大家有时间看Message Queue, 就会有更深的了解。

5.4 观察者模式的扩展

Java世界中的观察者模式

Java从一开始诞生就提供了一个可扩展的父类,即java.util.Observable, 这个类就是为那些“暴露狂”准备的,他们老是喜欢把自己的状态变更让别人去欣赏, 去触发。 我们打开Java的帮助文件看看, 查找一下Observable是不是已经有这个类了? JDK中提供了:java.util.Observable实现类和java.util.Observer接口, 也就是说我们上面写的那个例子中的Observable接口可以改换成java.util.Observale实现类了。

public class Observable {private boolean changed = false;private Vector<Observer> obs;/** Construct an Observable with zero Observers. */public Observable() {obs = new Vector<>();}/*** Adds an observer to the set of observers for this object, provided* that it is not the same as some observer already in the set.* The order in which notifications will be delivered to multiple* observers is not specified. See the class comment.** @param   o   an observer to be added.* @throws NullPointerException   if the parameter o is null.*/public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}/*** Deletes an observer from the set of observers of this object.* Passing <CODE>null</CODE> to this method will have no effect.* @param   o   the observer to be deleted.*/public synchronized void deleteObserver(Observer o) {obs.removeElement(o);}/*** If this object has changed, as indicated by the* <code>hasChanged</code> method, then notify all of its observers* and then call the <code>clearChanged</code> method to* indicate that this object has no longer changed.* <p>* Each observer has its <code>update</code> method called with two* arguments: this observable object and <code>null</code>. In other* words, this method is equivalent to:* <blockquote><tt>* notifyObservers(null)</tt></blockquote>** @see     java.util.Observable#clearChanged()* @see     java.util.Observable#hasChanged()* @see     java.util.Observer#update(java.util.Observable, java.lang.Object)*/public void notifyObservers() {notifyObservers(null);}/*** If this object has changed, as indicated by the* <code>hasChanged</code> method, then notify all of its observers* and then call the <code>clearChanged</code> method to indicate* that this object has no longer changed.* <p>* Each observer has its <code>update</code> method called with two* arguments: this observable object and the <code>arg</code> argument.** @param   arg   any object.* @see     java.util.Observable#clearChanged()* @see     java.util.Observable#hasChanged()* @see     java.util.Observer#update(java.util.Observable, java.lang.Object)*/public void notifyObservers(Object arg) {/** a temporary array buffer, used as a snapshot of the state of* current Observers.*/Object[] arrLocal;synchronized (this) {/* We don't want the Observer doing callbacks into* arbitrary code while holding its own Monitor.* The code where we extract each Observable from* the Vector and store the state of the Observer* needs synchronization, but notifying observers* does not (should not).  The worst result of any* potential race-condition here is that:* 1) a newly-added Observer will miss a*   notification in progress* 2) a recently unregistered Observer will be*   wrongly notified when it doesn't care*/if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);}/*** Clears the observer list so that this object no longer has any observers.*/public synchronized void deleteObservers() {obs.removeAllElements();}/*** Marks this <tt>Observable</tt> object as having been changed; the* <tt>hasChanged</tt> method will now return <tt>true</tt>.*/protected synchronized void setChanged() {changed = true;}/*** Indicates that this object has no longer changed, or that it has* already notified all of its observers of its most recent change,* so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.* This method is called automatically by the* <code>notifyObservers</code> methods.** @see     java.util.Observable#notifyObservers()* @see     java.util.Observable#notifyObservers(java.lang.Object)*/protected synchronized void clearChanged() {changed = false;}/*** Tests if this object has changed.** @return  <code>true</code> if and only if the <code>setChanged</code>*          method has been called more recently than the*          <code>clearChanged</code> method on this object;*          <code>false</code> otherwise.* @see     java.util.Observable#clearChanged()* @see     java.util.Observable#setChanged()*/public synchronized boolean hasChanged() {return changed;}/*** Returns the number of observers of this <tt>Observable</tt> object.** @return  the number of observers of this object.*/public synchronized int countObservers() {return obs.size();}
}
public interface Observer {/*** This method is called whenever the observed object is changed. An* application calls an <tt>Observable</tt> object's* <code>notifyObservers</code> method to have all the object's* observers notified of the change.** @param   o     the observable object.* @param   arg   an argument passed to the <code>notifyObservers</code>*                 method.*/void update(Observable o, Object arg);
}

5.5 项目中真实的观察者模式

为什么要说“真实”呢? 因为我们刚刚讲的那些是太标准的模式了, 在系统设计中会对观察者模式进行改造或改装, 主要在以下3个方面.

  • 观察者和被观察者之间的消息沟通

被观察者状态改变会触发观察者的一个行为, 同时会传递一个消息给观察者, 这是正确的, 在实际中一般的做法是: 观察者中的update方法接受两个参数, 一个是被观察者, 一个是DTO(Data Transfer Object, 据传输对象) , DTO一般是一个纯洁的JavaBean,由被观察者生成, 由观察者消费。

当然, 如果考虑到远程传输, 一般消息是以XML或JSON格式传递。

  • 观察者响应方式

我们这样来想一个问题, 观察者是一个比较复杂的逻辑, 它要接受被观察者传递过来的信息, 同时还要对他们进行逻辑处理, 在一个观察者多个被观察者的情况下, 性能就需要提到日程上来考虑了, 为什么呢? 如果观察者来不及响应, 被观察者的执行时间是不是也会被拉长? 那现在的问题就是: 观察者如何快速响应? 有两个办法: 一是采用多线程技术, 甭管是被观察者启动线程还是观察者启动线程, 都可以明显地提高系统性能, 这也就是大家通常所说的异步架构; 二是缓存技术, 甭管你谁来, 我已经准备了足够的资源给你了, 我保证快速响应, 这当然也是一种比较好方案, 代价就是开发难度很大, 而且压力测试要做的足够充
分, 这种方案也就是大家说的同步架构。

  • 被观察者尽量自己做主

这是什么意思呢? 被观察者的状态改变是否一定要通知观察者呢? 不一定吧, 在设计的时候要灵活考虑, 否则会加重观察者的处理逻辑, 一般是这样做的, 对被观察者的业务逻辑doSomething方法实现重载, 如增加一个doSomething(boolean isNotifyObs)方法, 决定是否通知观察者, 而不是在消息到达观察者时才判断是否要消费.

5.6 订阅发布模型

观察者模式也叫做发布/订阅模型(Publish/Subscribe) , 如果你做过EJB(EnterpriseJavaBean) 的开发, 这个你绝对不会陌生。 EJB2是个折腾死人不偿命的玩意儿, 写个Bean要实现, 还要继承, 再加上那一堆的配置文件, 小项目还凑合, 你要知道用EJB开发的基本上都不是小项目, 到最后是每个项目成员都在骂EJB这个忽悠人的东西; 但是EJB3是个非常优秀的框架, 还是算比较轻量级, 写个Bean只要加个Annotaion就成了, 配置文件减少了, 而且也引入了依赖注入的概念, 虽然只是EJB2的翻版, 但是毕竟还是前进了一步。 在EJB中有3个类型的Bean: Session Bean、 Entity Bean和MessageDriven Bean, 我们这里来说一下MessageDriven Bean(一般简称为MDB) , 消息驱动Bean, 消息的发布者(Provider) 发布一个消息, 也就是一个消息驱动Bean, 通过EJB容器(一般是Message Queue消息队列) 通知订阅者做出回应, 从原理上看很简单, 就是观察者模式的升级版, 或者说是观察则模式的BOSS版。

六、最佳实践

观察者模式在实际项目和生活中非常常见, 我们举几个经常发生的例子来说明.

  • 文件系统

比如, 在一个目录下新建立一个文件, 这个动作会同时通知目录管理器增加该目录, 并通知磁盘管理器减少1KB的空间, 也就说“文件”是一个被观察者, “目录管理器”和“磁盘管理器”则是观察者。

  • 猫鼠游戏

夜里猫叫一声, 家里的老鼠撒腿就跑, 同时也吵醒了熟睡的主人, 这个场景中, “猫”就是被观察者, 老鼠和人则是观察者。

  • ATM取钱

比如你到ATM机器上取钱, 多次输错密码, 卡就会被ATM吞掉, 吞卡动作发生的时候, 会触发哪些事件呢? 第一, 摄像头连续快拍, 第二, 通知监控系统, 吞卡发生; 第三,初始化ATM机屏幕, 返回最初状态。 一般前两个动作都是通过观察者模式来完成的, 后一个动作是异常来完成。

  • 广播收音机

电台在广播, 你可以打开一个收音机, 或者两个收音机来收听, 电台就是被观察者, 收音机就是观察者。

java与设计模式-观察者模式相关推荐

  1. Java常用设计模式——观察者模式

    导航 一.行为描述 二.角色关系 三.代码示例 一.行为描述 观察者会观察特定对象的状态变化,一旦状态有所变化或产生特定条件,被观察对象会通知给观察者, 而观察者则会依据通知信息采取特定处理措施. 举 ...

  2. java设计模式--观察者模式(Observer)

    java设计模式--观察者模式(Observer) java设计模式--观察者模式(Observer) 观察者模式的定义: 定义对象间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖于它的 ...

  3. Java设计模式-观察者模式(订阅发布模式)

    Java设计模式-观察者模式(订阅发布模式) 一起来看 会了就当复习丫,不会来一起来看看吧. 很喜欢一句话:"八小时内谋生活,八小时外谋发展". 如果你也喜欢,让我们一起坚持吧!! ...

  4. Java描述设计模式(11):观察者模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.观察者模式 1.概念描述 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.观察者模式定义了一种一对多 ...

  5. 设计模式:java及spring观察者模式(有利于代码解耦)

    http://www.cnblogs.com/softidea/p/5716870.html 什么是ApplicationContext?  它是Spring的核心,Context我们通常解释为上下文 ...

  6. Java设计模式——观察者模式

    转自:http://www.cnblogs.com/shamgod/p/4588557.html 一.概述 childe类中的是关联监听者dad的,若要再增加监听者,会很不方便,且要修改代码.好的方法 ...

  7. 设计模式-观察者模式(Observer)-Java

    设计模式-观察者模式(Observer)-Java 目录 文章目录 1.前言 2.示例案例-多人联机对战游戏的设计 3.观察者模式概述 3.1.观察者模式定义 3.2.观察者模式结构 3.3.观察者模 ...

  8. 一起学设计模式 - 观察者模式

    观察者模式(ObserverPattern)属于 对象行为型模式的一种,定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新. 概述 观察者模式是一种 ...

  9. java观察者设计模式_Java中的观察者设计模式

    java观察者设计模式 在本教程中,我们将讨论Observer设计模式. 观察者模式有两个主要方面- 主题和观察者 . 当我们的系统有多个对象(称为观察者)时 ,将使用一个模式,即依赖于一个特定对象( ...

  10. 设计模式---观察者模式介绍与理解

    设计模式---观察者模式介绍与理解: 观察者模式原理:类似于定牛奶业务 1. 奶站,subject:登记注册,移除,通知(register,remove,notify) 2. 用户,observer: ...

最新文章

  1. ajax iframe实现文件上传,iframe实现Ajax文件上传效果示例
  2. 阿里内部禁用Executors创建线程池,为什么?
  3. Java格式化Date为字符串的高级写法
  4. java五种加密技术理解
  5. react学习(42)----react中的jsx表达对象
  6. 1x1 11b g n linux,基于RN1810下的2.4 GHz IEEE 802.11b/g/n无线模块
  7. Scala里Map()集合
  8. 线程、多线程和线程池,看完这些你就能全部搞懂了
  9. UVA 620 - Cellular Structure
  10. DataSet和ListT 泛型之间互相转换 (转载, 作者写的很好)
  11. Android开源库--Gson谷歌官方json解析库
  12. Andorid-ViewPage2 左右滑动
  13. Linux 压测工具 stress 安装下载
  14. VMware 8安装Mac OS X 10.7
  15. idea字体渲染为mac字体,和安装新字体
  16. 今日头条竖屏视频没有收益吗,今日头条竖版视频没收益怎么回事
  17. vite项目在jenkins自动打包报错:failed to load config from ../vite.config.js You installed esbuild on
  18. java上机实验作业 编写汽车类car,Java代写:CS103 Car Rental代做留学生SQL实验作业...
  19. 使用Python操作音频文件,提取音频特征
  20. 网络安全风险与防范方法

热门文章

  1. 解锁前端密码框常见功能做法
  2. 硬盘重装系统:电脑本地硬盘重装系统步骤
  3. 《数学之美》读书记录(一)
  4. 断舍离------活成自己喜欢的样子
  5. SQL函数入门--统计函数+分组函数
  6. Yuuki and a problem (树套树)
  7. 翡翠玉石微观世界,太美了!
  8. 台铁预计耗资120亿新台币改善东部危险弯道
  9. html5制作一个六角星,儿童节手工折纸六角星制作教程
  10. 深度学习之美(张玉宏)——第三章 机器学习三重门