观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
所谓观察者模式,其实就是为了实现 松耦合(Loosely Coupled)。
举个例子,当数据有更新,如 changed
方法被调用,用于更新 state
数据,比如温度、气压等。
这些的问题是,如果向更新更多的信息,比如说湿度,那就要去修改 changed
方法的代码,这就是紧耦合的坏处。
对于观察者模式,我们仅仅维护一个可观察对象即可,即一个 Observable 实例,当有数据变更时,它只需维护一套观察者(Observer)的集合,这些 Observer 实现相同的接口,Subject 只需要指导,通知 Observer 时,需要调用哪个同一方法就好了。
解决问题:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作
应用时机:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知
如何解决:使用面向对象技术,可以将这种依赖关系弱化
类比实例:
优点:
缺点:
观察者模式包含如下角色:
// 被观察者class Subject {constructor(name) {// 观察者队列this.observers = [];}// 注册观察者到被观察者上attach(observer) {this.observers.push(observer);}// 执行所有观察者的 update 方法notify(nextState) {this.observers.forEach((o) => o.update(nextState));}}// 观察者class Observer {constructor(name) {this.name = name;}update(nextState) {console.log('通知:被观察已更新');}}// 创建被观察者const subject = new Subject();// 收到广播时要执行的方法const update = () => console.log('被观察者发出通知');// 观察者 1const obs1 = new Observer(update);// 观察者 2const obs2 = new Observer(update);// 观察者 1 订阅 subject 的通知subject.attach(obs1);// 观察者 2 订阅 subject 的通知subject.attach(obs2);// 发出广播,执行所有观察者的 update 方法subject.notify();
订阅发布模式(Pub-Sub Pattern)属于广义上的观察者模式
发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式。
以此避免发布者和订阅者之间产生依赖关系。
class EventEmitter {constructor() {this.listeners = {};}on(type, cb, options) {if (typeof type !== 'string' || typeof cb !== 'function') return false;this.listeners[type] = this.listeners[type] || [];this.listeners[type].push({cb,priority: options.priority || 0,});return () => {this.listeners = this.listeners.filter((item) => item.cb !== cb);};}off(type, cb) {if (typeof type !== 'string' || typeof cb !== 'function') return false;if (!this.listeners[type] || this.listeners[type].length === 0) return false;for (let i = 0; i < this.listeners[type].length; i++) {if (this.listeners[type][i] === cb) {this.listeners[type].splice(i, 1);}}}emit(type, data) {if (typeof type !== 'string') return false;this.listeners[type] &&this.listeners[type].sort((a, b) => a.priority - b.priority).forEach((item) => item.cb.call(null, data));}}
参考资料: