定义
观察者模式(Observer) 又称发布-订阅模式(Publish-Subscribe:Pub/Sub)是一种行为设计模式。
它是一种通知机制, 定义了对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知,并被自动更新。
实现关键:在被观察对象中有一个 ArrayList 来存放观察者们,并在适当时机进行通知它们更新。
实现方式
JAVA 实现
- Observer 抽象类
- 观察者:继承了 Observer 抽象类的实体类
- 被观察对象:Subject 类
- 创建 Subject 类(被观察对象)
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public void attach(Observer observer){
observers.add(observer);
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
- 创建 Observer 抽象类、观察者类
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
public class BinaryObserver extends Observer {
public BinaryObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
// 将state数据转二进制
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}
}
public class HexaObserver extends Observer{
public HexaObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
// 将state数据转十六进制
System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() );
}
}
- 使用
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
// 将观察者添加到被观察者的 ArrayList 中进行注册
new BinaryObserver(subject);
new OctalObserver(subject);
subject.setState(15);
// 输出:
// Binary String: 1111
// Hex String: F
}
}
JS 版本实现
- 被观察对象:维护了一个观察者的数组,新增时将观察者向数组中push;然后通过notify通知所有的观察者
- 观察者对象:维护 update 函数,用来接收观察者更新后的一个回调
// 定义被观察者
class Subject {
constructor() {
// 保存观察者数组
this.Observers = [];
}
add(observer) {
// 添加
this.Observers.push(observer);
}
notify() {
//通知所有观察者
this.Observers.forEach((item) => {
item.update();
});
}
}
// 定义观察者对象
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`my name is:${this.name}`);
}
}
let sub = new Subject();
let obs1 = new Observer("observer1");
sub.add(obs1);
let obs2 = new Observer("observer2");
sub.add(obs2);
// 通知所有观察者
sub.notify();
应用场景
1. DOM 事件
比如我们给 div 绑定 click 监听事件,其本质就是观察者模式的一种应用。
我们没办法预知用户将在什么时候点击,所以订阅 document.body 上的 click 事件,当 body 节点被点击时,body 节点便会向观察者发布这个消息。
document.body.addEventListener('click', function() {
alert(2);
}, false);
document.body.click(); // 模拟用户点击
2. 常见的状态管理工具
前端比较流程的状态管理工具 Redux 和 MobX 都是基于观察者模式的一种实现。
利用观察者模式(发布/订阅模式)连接 model 和 view 的中间对象
- view 层通过调用 store.dispatch 方法触发 reducer 改变 model 数据 -- 通知发布
- model 层通过调用 store.subscribe 注册视图更新事件(setstate)-- 订阅
3. Vue 中的观察者模式
Vue 中 model 层与 view 层进行通信也是使用了观察者模式。
视图(view)的渲染副作用会作为观察者被响应式数据系统(model)收集起来,当响应式数据发生变化时,会通知该数据对应的所有观察者,执行渲染副作用刷新 UI。
对比其他设计模式
责任链模式、 命令模式、 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:
- 责任链 按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。
- 命令 在发送者和请求者之间建立单向连接。
- 中介者 清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
- 观察者 允许接收者动态地订阅或取消接收请求。
中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。
\
优缺点
✔️ 开闭原则。无需修改发布者代码就能引入新的订阅者类
✔️ 可以在运行时建立对象之间的联系
❌ 订阅者的通知顺序是随机的
❌ 创建订阅者本身要消耗一定的时间和内存
总结
- 观察者模式(发布/订阅模式)在实际开发中应用非常广泛。既可以用在异步编程中,也可以帮助我们完成更松耦合的代码编写。
- 观察者模式还可以用来帮助实现一些别的模式,比如中介者模式。
- 从架构上来看无论是是 MVC 还是 MVVM ,都少不了发布/订阅模式的参与。