观察者模式
当对象间存在一对多的关系时,则使用观察者模式。比如当一个对象被修改的时候,则会自动通知依赖它的对象。观察者模式属于行为型模式。
目的
建立一对多的关联关系,并能使一个对象的变化被所有关联对象感知
动机
建立一套低耦合的消息触发机制
优点
- 被观察者和观察者之间是抽象耦合的
- 耦合度较低,两者之间的关联仅仅在于消息的通知
- 被观察者无需关心他的观察者
- 支持广播通信
缺点
- 观察者只知道被观察对象发生了变化,但不知变化的过程和缘由
- 观察者同时也可能是被观察者,消息传递的链路可能会过长,完成所有通知花费时间较多
- 如果观察者和被观察者之间产生循环依赖,或者消息传递链路形成闭环,会导致无限循环
适用场景
手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class Subject { constructor(name) { this.name = name; this.observers = []; this.state = 'I am always 18 years old.'; }
attach(observer) { this.observers.push(observer); }
setState(newState) { this.state = newState; this.observers.forEach(obj => obj.update(newState)); } }
class Observer { constructor(name) { this.name = name; }
update(newState) { console.log(this.name + ' say ' + newState); } }
let p1 = new Subject('Katrina'); let p2 = new Observer('Kate');
p1.attach(p2);
p1.setState('I am 6 years old!');
|
简单总结
被观察者维护一个observers的队列,并通过attach方法接收观察者,通过setState方法改变状态,并且通知所有依赖的观察者进行update
发布/订阅模式
发布订阅模式是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都会得到状态改变的通知。
基本过程
订阅者(Subscriber)把自己想订阅的事件注册到(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布到该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。
特点
订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。
实现思路
- 创建一个EventEmitter类
- 在该类上创建一个事件中心(Map)存储
- on方法用来把函数fn都加到事件中心里面(订阅者注册事件)
- emit方法取到arguments里第一个当作event,根据event值去执行对应事件中心中的函数(发布者发布事件,调度中心处理)
- off方法用于取消订阅
- once方法表示只监听一次,调用完毕后删除缓存函数(订阅一次)
- 注册一个newListener用于监听新的事件订阅
优点
一方面实现了发布者与订阅者之间的解耦,中间者可在两者操作之间进行更细粒度的控制。如:条件过滤发布,权限控制等等
缺点
整一个中间调度会越来越庞大,需要手动清除里面的发布回调
适用场景
手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| class EventEmitter { constructor() { this._events = {}; } on(eventName, callback) { const callbacks = this._events[eventName] || []; callbacks.push(callback); this._events[eventName] = callbacks; } emit(eventName, ...args) { const callbacks = this._events[eventName] || []; callbacks.forEach(cb => cb(...args)); } once(eventName, callback) { const one = (...args) => { callback(...args); this.off(eventName, one); } one.initialCallback = callback; this.on(eventName, one); } off(eventName, callback) { const callbacks = this._events[eventName] || []; const newCallbacks = callbacks.filter(fn => fn !== callback && fn.initialCallback !== callback ); this._events[eventName] = newCallbacks; } }
let events = new EventEmitter();
events.on('sayHi', function() { console.log('Katrina sya hello'); })
let cbFun = function() { console.log('I am here.'); }
events.on('hello', cbFun);
function once() { console.log('once'); }
events.once('hello', once);
events.emit('hello');
|
参考
观察者模式和订阅发布模式是一样的吗?