博主按:《天天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript
(_靠这吃饭_)和python
(_纯粹喜欢_)两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式 :)
订阅-发布模式定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖它的对象均可以获得通知。
了解过事件机制或者函数式编程的朋友,应该会体会到“订阅-发布模式”所带来的“时间解耦”和“空间解耦”的优势。借助函数式编程中闭包和回调的概念,能够很优雅地实现这种设计模式。javascript
订阅-发布模式和观察者模式概念类似,但在订阅-发布模式中,订阅者和发布者之间多了一层中间件:一个被抽象出来的信息调度中心。java
但其实没有必要太深究 2 者区别,由于《Head First 设计模式》这本经典书都写了:发布+订阅=观察者模式。其核心思想是状态改变和发布通知。在此基础上,根据语言特性,进行实现便可。python
python 中咱们定义一个事件类Event
, 而且为它提供 事件监听函数、(事件完成后)触发函数,以及事件移除函数。任何类均可以经过继承这个通用事件类,来实现“订阅-发布”功能。git
class Event: def __init__(self): self.client_list = {} def listen(self, key, fn): if key not in self.client_list: self.client_list[key] = [] self.client_list[key].append(fn) def trigger(self, *args, **kwargs): fns = self.client_list[args[0]] length = len(fns) if not fns or length == 0: return False for fn in fns: fn(*args[1:], **kwargs) return False def remove(self, key, fn): if key not in self.client_list or not fn: return False fns = self.client_list[key] length = len(fns) for _fn in fns: if _fn == fn: fns.remove(_fn) return True # 借助继承为对象安装 发布-订阅 功能 class SalesOffice(Event): def __init__(self): super().__init__() # 根据本身需求定义一个函数:供事件处理完后调用 def handle_event(event_name): def _handle_event(*args, **kwargs): print("Price is", *args, "at", event_name) return _handle_event if __name__ == "__main__": # 建立2个回调函数 fn1 = handle_event("event01") fn2 = handle_event("event02") sales_office = SalesOffice() # 订阅event01 和 event02 这2个事件,而且绑定相关的 完成后的函数 sales_office.listen("event01", fn1) sales_office.listen("event02", fn2) # 当两个事件完成时候,触发前几行绑定的相关函数 sales_office.trigger("event01", 1000) sales_office.trigger("event02", 2000) sales_office.remove("event01", fn1) # 打印:False print(sales_office.trigger("event01", 1000))
JS 中通常用事件模型来代替传统的发布-订阅模式。任何一个对象的原型链被指向Event
的时候,这个对象即可以绑定自定义事件和对应的回调函数。github
const Event = { clientList: {}, // 绑定事件监听 listen(key, fn) { if (!this.clientList[key]) { this.clientList[key] = []; } this.clientList[key].push(fn); return true; }, // 触发对应事件 trigger() { const key = Array.prototype.shift.apply(arguments), fns = this.clientList[key]; if (!fns || fns.length === 0) { return false; } for (let fn of fns) { fn.apply(null, arguments); } return true; }, // 移除相关事件 remove(key, fn) { let fns = this.clientList[key]; // 若是以前没有绑定事件 // 或者没有指明要移除的事件 // 直接返回 if (!fns || !fn) { return false; } // 反向遍历移除置指定事件函数 for (let l = fns.length - 1; l >= 0; l--) { let _fn = fns[l]; if (_fn === fn) { fns.splice(l, 1); } } return true; } }; // 为对象动态安装 发布-订阅 功能 const installEvent = obj => { for (let key in Event) { obj[key] = Event[key]; } }; let salesOffices = {}; installEvent(salesOffices); // 绑定自定义事件和回调函数 salesOffices.listen( "event01", (fn1 = price => { console.log("Price is", price, "at event01"); }) ); salesOffices.listen( "event02", (fn2 = price => { console.log("Price is", price, "at event02"); }) ); salesOffices.trigger("event01", 1000); salesOffices.trigger("event02", 2000); salesOffices.remove("event01", fn1); // 输出: false // 说明删除成功 console.log(salesOffices.trigger("event01", 1000));