JS设计模式(4):观察者模式
一、引入
在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如:
- 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;
- 新闻网站中,发布新新闻时需要推送给所有订阅该频道的读者;
- 股票行情系统中,股票价格变动时需要实时通知所有关注该股票的投资者。
这些场景的共同特点是:多个对象之间存在依赖关系,一个对象的状态改变会影响到其他对象。如果直接在代码中硬编码这些依赖关系,会导致代码耦合度高、难以维护和扩展。此时,观察者模式(Observer Pattern)就能很好地解决这类问题。
读完本文,你将学会如何利用观察者模式实现对象间的消息通信,让代码在保持低耦合的同时,具备更强的可维护性和扩展性。
二、何为观察者模式
观察者模式是一种行为型设计模式,其核心思想是:定义对象间的一种一对多依赖关系,使得当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都能自动收到通知并执行相应的操作。
核心角色:
- 被观察者(Subject):维护观察者列表,提供添加、删除观察者的方法,当状态变化时通知所有观察者。
- 观察者(Observer):定义接收通知的接口,通常包含一个更新方法,用于在收到通知时执行具体操作。
实现思路:
通过Proxy代理对象实现数据劫持,当被观察对象的属性发生变化时,自动触发通知逻辑,通知所有注册的观察者。
三、代码实现
我们以一个简单的人物信息对象为例,演示观察者模式的具体实现:
// 原始数据对象
const person = {
name: 'ts',
age: 21
}
// 被观察者类
class Subject {
constructor(value) {
this.observers = []; // 存储观察者列表
// 使用Proxy实现数据代理,监听属性变化
const handler = {
get: (self, key) => {
return self[key];
},
set: (self, key, newVal) => {
// 打印属性变化信息
console.log(`${key}从${self[key]}修改为${newVal}`);
// 触发通知
this.notify(key, newVal);
self[key] = newVal; // 更新属性值
return true;
}
};
this.value = new Proxy(value, handler); // 生成代理对象
}
// 添加观察者
add(observer) {
this.observers.push(observer);
}
// 通知所有观察者
notify(key, newVal) {
this.observers.forEach(observer => observer.update(key, newVal));
}
}
// 观察者类
class Observer {
constructor(value) {
this.value = value; // 观察者自身的数据
}
// 更新方法,收到通知时执行
update(key, value) {
console.log(`${this.value.name}观察到${key}变化了,新的值是${value}`);
this.value[key] = value; // 更新观察者自身的数据
}
}
// 创建被观察者实例,传入原始数据
const proxyPerson = new Subject(person);
// 创建观察者实例
const obs1 = new Observer({ name: 'xll', age: 100 });
const obs2 = new Observer({ name: 'xhh', age: 3 });
// 注册观察者
proxyPerson.add(obs1);
proxyPerson.add(obs2);
// 修改被观察对象的属性,触发通知
proxyPerson.value.age = 10;
proxyPerson.value.name = 'js';
执行结果:
age从21修改为10
xll观察到age变化了,新的值是10
xhh观察到age变化了,新的值是10
name从ts修改为js
xll观察到name变化了,新的值是js
xhh观察到name变化了,新的值是js
四、代码解析
被观察者(Subject):
- 通过
Proxy
对原始数据对象进行代理,重写get
和set
方法,实现对属性访问和修改的拦截。 - 当属性被修改时,调用
notify
方法通知所有观察者。 add
方法用于注册观察者,将观察者添加到观察者列表中。
- 通过
观察者(Observer):
- 每个观察者都有自己的状态数据(如
obs1
和obs2
的name
和age
)。 update
方法是观察者的核心逻辑,用于在收到通知时更新自身状态,并执行相应的业务操作(如打印日志)。
- 每个观察者都有自己的状态数据(如
解耦与通知流程:
- 被观察者和观察者之间通过接口进行通信,彼此无需知道对方的具体实现,实现了低耦合。
- 当被观察对象的属性发生变化时,通过
Proxy
自动触发通知,观察者被动接收通知并更新状态,形成了“数据变化→通知→更新”的完整流程。
五、观察者模式的优点
维度 | 观察者模式 | 直接调用方式 |
---|---|---|
耦合性 | 低耦合,对象间无需直接依赖 | 高耦合,对象间直接调用 |
扩展性 | 新增观察者无需修改被观察者 | 新增功能需修改原始代码 |
复用性 | 观察者可复用于多个被观察者 | 功能复用性差 |
灵活性 | 可动态添加/删除观察者 | 功能绑定固定,难以调整 |
六、应用场景
- 状态监控:监控服务器状态、设备状态等,当状态变化时通知相关系统或人员。
- 消息订阅:实现消息发布-订阅系统,如邮件订阅、短信通知等。
- 数据绑定:前端框架中的数据双向绑定(如Vue的响应式原理),本质上就是观察者模式的应用。
- 事件驱动:浏览器中的事件监听(如点击事件、键盘事件等),也是观察者模式的典型场景。
通过观察者模式,我们可以将对象间的依赖关系从紧耦合变为松耦合,使系统更具灵活性和可扩展性。在后续的文章中,我们将继续探讨其他设计模式,帮助你写出更优雅、更易维护的代码。如果你有任何问题或建议,欢迎在评论区留言!