1. 什么是“订阅-发布模式”?
订阅-发布模式定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都可以得到通知。
了解过事件机制或者函数式编程的朋友,应该会体会到“订阅-发布模式”所带来的“时间解耦”和“空间解耦”的优点。借助函数式编程中闭包和回调的概念,可以很优雅地实现这种设计模式。
2. “订阅-发布模式” vs 观察者模式
订阅-发布模式和观察者模式概念相似,但在订阅-发布模式中,订阅者和发布者之间多了一层中间件:一个被抽象出来的信息调度中心。
但其实没有必要太深究 2 者区别,因为《Head First 设计模式》这本经典书都写了:发布+订阅=观察者模式。其核心思想是状态改变和发布通知。在此基础上,根据语言特性,进行实现即可。
3. 代码实现
3.1 python3 实现
python 中我们定义一个事件类Event
, 并且为它提供 事件监听函数、(事件完成后)触发函数,以及事件移除函数。任何类都可以通过继承这个通用事件类,来实现“订阅-发布”功能。
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
| 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__": fn1 = handle_event("event01") fn2 = handle_event("event02")
sales_office = SalesOffice()
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)
print(sales_office.trigger("event01", 1000))
|
3.2 ES6 实现
JS 中一般用事件模型来代替传统的发布-订阅模式。任何一个对象的原型链被指向Event
的时候,这个对象便可以绑定自定义事件和对应的回调函数。
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 74 75 76 77 78 79 80 81 82 83 84 85
| 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)
console.log(salesOffices.trigger('event01', 1000))
|
4. 参考