观察者设计模式
定义
一种行为型设计模式,用于在对象之间建立一种一对多的依赖关系 ,当一个对象的状态发生变化 时,它的所有依赖对象 都会收到通知并自动更新。
参与者
<math xmlns="http://www.w3.org/1998/Math/MathML"> 1. \color{red}{1.} </math>1. 主题 (Subject):也称为被观察者或可观察对象 ,它维护 着一组观察者对象,并提供了添加、删除和通知观察者的方法。主题可以是具体的类或接口。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 2. \color{red}{2.} </math>2. 观察者 (Observer):也称为订阅者,它定义了接收和处理主题通知的方法 。观察者通过注册到主题上来接收通知,并根据通知进行相应的操作。观察者可以是具体的类或接口。
在观察者设计模式中,主题和观察者之间是松耦合的关系。主题并不直接调用观察者的方法,而是通过通知的方式告知观察者发生了变化。观察者通过注册到主题上来表明自己感兴趣的事件,并在事件发生时接收到通知。
核心思想
解耦发布者 和订阅者 ,使得两者之间能够独立变化 。当一个对象的状态发生变化时,所有依赖于它的观察者都会得到通知,并自动更新以反映最新的状态。
应用场景
- GUI 事件处理 :图形用户界面(GUI)中的按钮、菜单项和其他控件通常作为主题,可以被不同的观察者订阅,以响应用户的交互操作。
- 消息队列系统 :在消息队列系统中,生产者 发送消息到主题,而消费者作为观察者订阅主题来接收和处理消息。
- 数据库通知机制 :数据库中的触发器 和事件通知机制 可以使用观察者模式来实现。触发器可以充当主题,而订阅该触发器的存储过程或应用程序可以作为观察者进行相应的处理。
- 股票市场更新 :股票市场中的投资者 可以作为观察者订阅特定股票的主题,以接收价格更新的通知。
示例
typescript
// 主题接口
interface Subject {
registerObserver(observer: Observer): void;
removeObserver(observer: Observer): void;
notifyObservers(): void;
}
// 观察者接口
interface Observer {
update(data: any): void;
}
// 具体主题类
class ConcreteSubject implements Subject {
private observers: Observer[] = [];
private data: any;
registerObserver(observer: Observer): void {
this.observers.push(observer);
}
removeObserver(observer: Observer): void {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
notifyObservers(): void {
for (const observer of this.observers) {
observer.update(this.data);
}
}
setData(data: any): void {
this.data = data;
this.notifyObservers();
}
}
// 具体观察者类
class ConcreteObserver implements Observer {
private name: string;
constructor(name: string) {
this.name = name;
}
update(data: any): void {
console.log(`Observer ${this.name} received data: ${data}`);
}
}
// 客户端代码
const subject = new ConcreteSubject();
const observer1 = new ConcreteObserver("Observer 1");
const observer2 = new ConcreteObserver("Observer 2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setData("Hello world!");
前端应用
<math xmlns="http://www.w3.org/1998/Math/MathML"> 1. \color{red}{1.} </math>1. 事件处理 :在 JavaScript 和浏览器中,事件机制就是观察者设计模式的实现 。DOM 元素 可以作为主题 ,而事件监听函数 则充当观察者。当事件发生时,主题会通知所有注册的观察者执行相应的事件处理函数。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 2. \color{red}{2.} </math>2. AJAX 请求 :在使用 XMLHttpRequest 或 Fetch API 发起 AJAX 请求时,我们可以将请求对象 作为主题 ,而回调函数 作为观察者。当请求返回结果时,主题会通知观察者进行相应的处理。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 3. \color{red}{3.} </math>3. 订阅-发布(Pub/Sub)模式:该模式是观察者模式的一种变体,在 JavaScript 中被广泛应用于消息传递和事件系统。通过订阅和发布机制,多个订阅者可以同时订阅特定的主题,并在主题发生变化时接收通知。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 4. \color{red}{4.} </math>4. 数据绑定和响应式框架 :许多前端框架和库,如 Vue.js 和 React.js ,使用观察者模式来实现数据绑定和响应式更新。当数据发生变化时,框架会通知相关的观察者组件,使其自动更新视图。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 5. \color{red}{5.} </math>5. WebSocket 通信 :WebSocket 是一种双向通信协议,利用观察者模式来实现实时消息推送。服务器 可以作为主题 ,而客户端 则充当观察者。当服务器有新消息时,它会通知所有连接的客户端。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 6. \color{red}{6.} </math>6. Node.js 事件驱动编程 :Node.js 是基于事件驱动的非阻塞 I/O 模型。它使用观察者模式来处理事件 和回调函数。当特定的事件发生时,Node.js 会通知相应的观察者执行对应的回调函数,以实现异步和并发操作。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 7. \color{red}{7.} </math>7. 第三方库 :MobX 是一个 JavaScript 库,用于管理状态(state)和处理状态变化。它使用了观察者模式来跟踪对状态 的更改,并使得相关组件能够自动地进行更新。
ES6 Observers
ES6 中引入了五种 Observer,可以看成是对订阅者模式的实践,它们分别为:
<math xmlns="http://www.w3.org/1998/Math/MathML"> 1. \color{red}{1.} </math>1. ResizeObserver:用于监测元素尺寸变化。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 2. \color{red}{2.} </math>2. MutationObserver:用于监测 DOM 树的变化。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 3. \color{red}{3.} </math>3. ReportingObserver:用于监测报告类型的变化。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 4. \color{red}{4.} </math>4. PerformanceObserver:用于监测页面性能指标。
<math xmlns="http://www.w3.org/1998/Math/MathML"> 5. \color{red}{5.} </math>5. IntersectionObserver:用于监测元素是否进入视口或与其交叉。
对于上面的这五个 Observer 的使用,我会在下一篇文章中介绍!