1. 概述
有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
2. 为什么用
系统开发过程总,后台需要多个类来共同协作完成,这些类相互协作的对象就要保持一致性。说白了,观察者设计模式就是维护对象状态一致性的。那么不用这种模式完全可以解决复杂的业务逻辑。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
处理连锁反应,在设计模式之禅看到作者说了这么一段话:我们去打猎,打死了一只母鹿,母鹿有三个幼崽,因失去了母鹿而饿死,尸体又被两只秃鹰争抢,因分配不均,秃鹰开始斗殴,然后羸弱的秃鹰死掉,生存下来的秃鹰,则因此扩大了地盘……这就是一个触发机制,形成了一个触发链。观察者模式可以完美地实现这里的链条形式。这听起来有点像触发器。
3. 是什么
模式中的角色
被观察者(Subject):拥有观察者对象们的集合(可以实现观察者的任意增减)和被观察者自身的属性和活动(使用过程中可以将观察者的增删操作从被观察者类里单独出来,这样让原来的被观察者只存在自身属性),
具体的被观察者(ConcreteSubject):实现被观察者定义接口,在内部状态改变时,给所有登记过的观察者发出通知。
观察者(Observer):为所有的具体观察者定义一个接口,在得到被观察者通知时更新。
具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
4. 模式类图
5. 怎么用
被观察者:
package com.liyb.pattern.observer.Impl;
import java.util.ArrayList;
import java.util.List;
/**
* @description:被观察者
* @author: lee
* @日期:2016年7月15日
*/
public class Subject {
//保存观察者集合对象
List<Observers> list = new ArrayList<Observers>();
//增加一个观察者
public void addObserver(Observers o){
list.add(o);
}
//删除一个观察者
public void dellObserver(Observers o){
list.remove(o);
}
//被观察者的活动
public void dosomething(){
System.out.println("被观察者:我要去旅游。");
notifyObserver();//被观察者是主动触发
}
//通知观察者
public void notifyObserver(){
for(Observers obs : list){//取出观察者,依次通知
obs.receive("通知:有行动了...");
}
}
}
package com.liyb.pattern.observer.Impl;
/**
*
* @description: 观察者
* @author: lee
* @日期:2016年7月15日
*/
public class Observers {
//观察者观察到被观察者的行动后采取的措施
public void receive(String context){
System.out.println("观察者:我已经收到你发来的电文,内容是 "+context);
System.out.println("观察者:现在执行A计划。");
}
}
测试:
package com.liyb.pattern.observer.Impl;
public class Test {
public static void main(String[] agrs){
//创建被观察者
Subject subject = new Subject();
//创建观察者
Observers observer = new Observers();
//观察者观察被观察者
subject.addObserver(observer);
//被观察者有活动马上就被观察者发现了
subject.dosomething();
}
}
以上就是观察者模式的基本实现,当然我实际的开发运用中,需要把观察者和被观察者用接口或者抽象类来实现,把被观察者中关于操观察者的部分分离出来,自身只实现dosometing。之所以这么也是让自己日后更加清晰的理解。
6. 优点和缺点
在敲完上面的代码后,我立刻感觉到一丝丝不爽。首先被观察者是主动出发自身方法去通知观察者的,其次没有实现无代码植入的监控。在来看上面的一段代码:
for(Observers obs : list){//取出观察者,依次通知
<span style="white-space:pre"> </span>obs.receive("通知:有行动了...");
}
在依次通知每个观察者的时候,如果其中一个观察者出现问题会影响整体执行效率。多级触发的效率是我们在使用它时需要考虑的一个问题。在程序设计的时候还要考虑实体即是观察者又是被观察者的情况,错综复杂的情况也给开发带来了一定的难度。
当然观察者模式作为一个常用的优秀的编码习惯,优点也很明显,观察者和被观察者都非常容易扩展(当多个观察者观察时很明显),观察者和被观察者是抽象层及的耦合。
7. 使用场景
1、关联行为场景,关联行为是可拆分的,而不是组合。
2、事件多级触发。
3、跨系统的消息交换,如消息队列处理机制。
7. 使用观察者模式的注意事项(这段话出自《设计模式之禅 2》)
广播链的问题
如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表A上写了一个触发器,内容是一个字段更新后更新表B的一条数据,要更新表C,表C也有触发器...完蛋了,这个数据库基本上就毁掉了,我们的观察者模式也有一样的问题,一个观察者可以有双重身份,既可以是观察者也可以是被观察者,这没什么问题,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,字啊一个观察者模式中最多出现一个对象即是观察者又是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的。
注意:它和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点些上的消息结构。而责任链模式在消息传递的过程中基本上保持消息不可变,如果改变,也只是在原有的消息上进行修改。
异步处理的问题
这个EJB是个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者较多,而且处理时间比较长,那就用异步,异步就要考虑线程安全和安全队列的问题。