二十一、观察者模式(observer)
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法,观察者为所有的具体观察者定义一个接口,在得到主题的通知时更新自己
ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;
ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。
Implementation
主题Subject
首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。
1 | public class Subject { |
抽象观察者Observer
观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。
1 | public interface Observer { |
具体主题
继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
1 | public class ConcreteSubject extends Subject { |
具体观察者
实现Observer接口。
1 | public class ConcreteObserver implements Observer { |
Client客户端
首先创建一个被观察者,然后定义一个观察者,将该被观察者添加到该观察者的观察者数组中,进行测试。
1 | public class Client { |
开发中常见的场景
聊天室程序的,服务器转发给所有客户端
网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发
邮件订阅
Servlet中,监听器的实现
Android中,广播机制
JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event Model)
事件源—————-目标对象
事件监听器————观察者
京东商城中,群发某商品打折信息
观察者模式的应用
1. 何时使用
- 一个对象状态改变,所有的依赖对象都将得到通知
2. 方法
- 使用面向对象技术
3. 优点
- 观察者和被观察者是抽象耦合的
- 建立了一套触发机制
4. 缺点
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
- 如果观察者和观察目标间有循环依赖,可能导致系统崩溃
- 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的
5. 使用场景
- 关联行为场景
- 事件多级触发场景
- 跨系统的消息变换场景,如消息队列的处理机制
6. 应用实例
- 手机丢了,委托别人给其他人发消息通知
- 通知老师/老板来了
- 拍卖,拍卖师观察最高标价,然后通知给其它竞价者竞价
- 在一个目录下建立一个文件,会同时通知目录管理器增加目录,并通知磁盘减少空间,文件是被观察者,目录管理器和磁盘管理器是观察者
- 猫叫了一声,吓着了老鼠,也惊到了主人,猫是被观察者,老鼠和人是观察者
7. 注意事项
- 避免循环引用
- 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式
二十二、备忘录模式(Memento)
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
- Originator:原始对象
- Caretaker:负责保存好备忘录
- Memento:备忘录,存储原始对象的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。
Implementation
发起人角色
记录当前时刻的内部状态,并负责创建和恢复备忘录数据,允许访问返回到先前状态所需的所有数据。
1 | public class Originator { |
备忘录角色
负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
1 | public class Memento { |
备忘录管理员角色
对备忘录进行管理、保存和提供备忘录,只能将备忘录传递给其他角色。
1 | public class Caretaker { |
Client客户端
下面编写一小段代码测试一下,即先将状态置为On,保存后再将状态置为Off,然后通过备忘录管理员角色恢复初始状态。
1 | public class Client { |
备忘录模式的应用
1. 何时使用
- 需要记录一个对象的内部状态时,为了允许用户取消不确定或者错误的操作,能够恢复到原先的状态
2. 方法
- 通过一个备忘录类专门存储对象状态
3. 优点
- 给用户提供了一种可以恢复状态的机制,可以使用能够比较方便地回到某个历史的状态
- 实现了信息的封装,使得用户不需要关心状态的保存细节
4. 缺点
- 消耗资源
5. 使用场景
- 需要保存和恢复数据的相关场景
- 提供一个可回滚的操作,如ctrl+z、浏览器回退按钮、Backspace键等
- 需要监控的副本场景
6. 应用实例
- 游戏存档
- ctrl+z键、浏览器回退键等(撤销/还原)
- 棋盘类游戏的悔棋
- 数据库事务的回滚
7. 注意事项
- 为了符合迪米特法则,需要有一个管理备忘录的类
- 不要在频繁建立备份的场景中使用备忘录模式。为了节约内存,可使用原型模式+备忘录模式