1. 观察者模式(Observer Pattern)
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. (定义对象间一种一对多的依赖关系, 使得当一个对象状态发生改变时,所有依赖于它的对象都会接收到通知并被自动更新)
观察者模式也称之为发布订阅模式, 也是一种项目中常用的设计模式.
1.1 观察者模式-核心思想
- 其实就是消息发布订阅模式
- 实现被观察者和观察者直接的解耦:
- 被观察者无须关注有哪些观察者,也不用关心观察者具体实现逻辑.
- 观察者无须关注什么时候,什么场景状态发生了变更,只需要等待消息变更通知即可.
1.2 观察者模式-类图
观察者模式有四种角色:
- Subject: 被观察者角色: 能够动态地管理(添加和删除)观察者, 通知观察者。通常为抽象类或接口.
- Observer: 观察者抽象接口, 定义更新方法.
- ConcreteSubject: 具体被观察者角色. 定义自己的业务逻辑,并实现哪些操作触发通知方法
- ConcreteObserver: 具体观察者.
- 维护一个指向ConcreteObserver 的引用, 当接收到通知时, 进行状态实时查询
- 实现更新接口
[外链图片转存失败(img-go3w6h6a-1566895375964)(https://raw.githubusercontent.com/zongf0504/blog-images/master/images/design-parttern/ldp-observer-01.png)]
1.3 观察者模式-时序图
- 触发更新状态方法: 标准观察者模式中, 观察者持有具体被观察者对象的引用ConcreteSubject, 因此也可以执行修改状态方法. 第1步也可以是其它客户端触发修改状态方法
- 通知方法: 修改状态方法内部触发通知观察者方法
- 按顺序同步执行第一个观察者的更新方法
- 观察者更新方法中,通过持有的具体观察者引用对象来查询最新的状态
- 返回给观察者当前状态,观察者自行处理
- 通知第二个观察者执行更新状态…
1.4 观察者模式-优缺点
- 优点:
- 被观察者与观察者直接松耦合. 标准模式中观察者中需要耦合具体被观察者引用.
- 建立了一套触发机制. 可实现消息广播机制
- 缺点:
- 消息触发时,同步执行,会有效率,异常等问题. 可考虑采取异步通知解决
- 并发问题. 观察者中接收到消息时,需要实时查询被观察者的状态,在多线程环境中,会导致并发问题.
1.5 观察者模式-适用场景
- 消息通知, 事件响应.
- 消息订阅,发布.
- 一个对象的变更需要通知多个对象,但是不知道具体通知的对象有哪些
1.6 注意事项
- 通知方法是按顺序执行,需要考虑效率和异常情况
- 任何一个观察者的更新方法出现问题时,都可能导致业务逻辑执行失败。可考虑异步线程
2. 观察者模式-应用示例
我们来假定一种应用场景,App和Web同时监听温度的变化.
2.1 类图
[外链图片转存失败(img-JqVB4GfL-1566895375966)(https://raw.githubusercontent.com/zongf0504/blog-images/master/images/design-parttern/ldp-observer-03.png)]
2.2 抽象被观察者-AbsSubject
- 从观察者模式标准类图来看,抽象被观察者角色应该是一个抽象类.因此笔者也采用抽象类来实现.
- 如果替换为接口会更灵活,更通用. 存储和遍历观察者的逻辑由子类实现
public abstract class AbsSubject {
// 储存所有的观察者
private LinkedList<Observer> observers = new LinkedList();
public void attach(Observer observer){
this.observers.add(observer);
}
public void detach(Observer observer){
this.observers.remove(observer);
}
public void notifyObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
2.3 具体被观察者-TemperatureSubject
- 监听状态为温度
- 当修改温度时,通知所有观察者
public class TemperatureSubject extends AbsSubject {
// 保存当前温度
private String temperature;
public void setTemperature(String temperature) {
this.temperature = temperature;
System.out.println("更新当前温度为:" + temperature + " 度");
// 通知观察者
this.notifyObservers();
}
public String getTemperature() {
return this.temperature;
}
}
2.4 抽象观察者-Observer
public interface Observer {
// 更新方法: 接收到通知后, 进行更新操作
public void update();
}
2.5 web应用观察者-WebObserver
当接收到应用时,会去查询实时状态, 会保证应用获取到真实的温度.
public class WebObserver implements Observer {
private TemperatureSubject concreteSubject;
public WebObserver(TemperatureSubject subject) {
this.concreteSubject = subject;
}
@Override
public void update() {
System.out.println("Web-接收到消息...");
// 查询状态
String state = concreteSubject.getTemperature();
System.out.println("Web-查询实时温度:" + state + " 度\n");
}
}
2.6 app 应用观察者-AppObserver
public class AppObserver implements Observer {
private TemperatureSubject concreteSubject;
public AppObserver(TemperatureSubject subject) {
this.concreteSubject = subject;
}
@Override
public void update() {
System.out.println("App-接收到消息...");
// 查询状态
String state = concreteSubject.getTemperature();
System.out.println("App-查询实时温度:" + state + " 度\n");
}
}
2.7 测试
@Test
public void test() {
// 创建监听主题
TemperatureSubject temperatureSubject = new TemperatureSubject();
// 添加观察者
temperatureSubject.attach(new WebObserver(temperatureSubject));
temperatureSubject.attach(new AppObserver(temperatureSubject));
// 更新状态
temperatureSubject.setTemperature("37");
}
2.8 测试输出
更新当前温度为:37 度
Web-接收到消息...
Web-查询实时温度:37 度
App-接收到消息...
App-查询实时温度:37 度