1.定义
定义对象之间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖它的对象都会得到通知并自动更新
观察者模式也叫发布订阅模式
2.观察者模式的使用场景
- 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。也就是说观察者的生命周期不由被观察者管理。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列的处理机制。
相信大家都看过无间道,那么应该对我下面抽象的土匪在警局里面安插卧底的代码不会陌生,这就非常形象地描述了观察者模式。
package _16ObserverPattern; import java.util.ArrayList; import java.util.List; /** * 抽象被观察者 */ public abstract class Observable { // 存放所有观察者 private List<Observer> observers = new ArrayList<Observer>(); // 增加观察者 public void addObserver(Observer observer) { this.observers.add(observer); } // 删除观察者 public void deleteObserver(Observer observer) { this.observers.remove(observer); } // 通知每个观察者 public void notifyObserver(String context) { for(Observer observer : observers) { observer.update(context); } } }
package _16ObserverPattern; /** * 抽象观察者 */ public abstract class Observer { public abstract void update(String context); }
package _16ObserverPattern; /** * 具体被观察者 */ public class Police extends Observable { public void action() { System.out.println("警察出动了"); // 被卧底知道,通知大哥 notifyObserver("警察出动,大哥快走"); } }
package _16ObserverPattern; /** * 具体观察者 */ public class Bandit extends Observer { @Override public void update(String context) { System.out.println("土匪收到消息:"+context+"。马上闪"); } }
package _16ObserverPattern; /** * 场景类 */ public class Test { public static void main(String[] args) { // 一个警察 Police police = new Police(); // 一个土匪卧底在警局 Bandit bandit = new Bandit(); police.addObserver(bandit); // 警察行动 police.action(); // 结果又卧底,消息泄露,土匪全跑了 } }
你执行以下就会发现,警察一出动,土匪全跑了。这就是观察者模式,简单吧。
其实在Java中已经有观察者和被观察者的抽象实现,分别是java.util.Observer接口和java.util.Observable抽象类,可以替代我们上面的实现。
3.观察者模式的四个角色
- Subject被观察者:定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
- Observer观察者:观察者接收到消息后,即进行update操作,对接收到的信息进行处理。
- ConcreteSubject具体的被观察者:定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
- ConcreteObserver具体的观察者:每个观察者在接收到消息后的处理反应不同,各个观察者有自己的处理逻辑。
4.观察者模式的优点
- 观察者和被观察者直接是抽象耦合:如此设计,则不管是增加观察者还是被观察者都非常容易扩展,而且在Java中已经实现了抽象层级的定义,在系统扩展方面更是得心应手。
- 建立一套触发机制:观察者模式可以完美地实现一个触发链。A触发B,B触发C,这里的B即是观察者(观察了A),又是被观察者(被C观察)。
5.观察者模式的缺点
观察者模式需要考虑一下开发效率和运行效率的问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在Java中的消息通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步(多线程)实现。多级触发的效率也比较低,设计时应注意。
6.观察者模式的注意事项
- 广播链问题 :如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表A上写了一个触发器,内容是一个字段更新后更新表B的一条数据,而表B上也有个触发器,要更新表C……这么触发下去就完蛋了,这个数据库被毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,即是观察者又是被观察者,链一旦建立,这个逻辑就非常复杂,可维护性极差。根据经验,在一个观察模式中出现最多一个双重身份的观察者,也就是说消息最多传递两次(转发一次),也还是比较好控制的。
- 异步处理问题 :如果观察者比较多采用了异步处理,那就要考虑线程安全和队列的问题。
它和责任链模式的最大区别就是观察者广播链在传播过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息的传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。
7.现实世界的观察者模式
- PC文件系统:增加一个文件以后要通知目录管理器增加该目录,并通知磁盘管理器减少空间。
- 广播收音机:电台是被观察者,收音机是观察者。