观察者模式与发布/订阅模式

观察者模式与发布/订阅模式

最近遇到观察者模式和发布/订阅模式的频率很高,就学习了一下,这里主要总结这种设计模式的由来,详细介绍他们以及区别。

提前声明一下,观察者模式出现较早,发布/订阅模式是观察者模式的一种变形。

来源

观察者模式是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,是一对多的依赖关系,在它本身的状态改变时主动发出通知,观察者收到通知从而使他们的状态自动发生变化。

观察者模式

目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。

观察者模式与发布/订阅模式

代码如下:

//目标
function Subject(){ 
    this.subjectList = []
}
Subject.prototype = {
    constructor: Subject,
	//注册
    add: function(fn){	
        this.subjectList.push(fn)
    },
	//发通知
    notify: function(context){	
        var subjectCount = this.subjectList.length
        for(var i=0; i < subjectCount; i++){
            this.subjectList[i].update(context)
        }
    },
	//取消注册
    remove: function(fn){
        this.subjectList.splice(this.subjectList.indexOf(fn),1)
    }
}
//观察者
function ObserverB(){    
    this.update = function(date){
        console.log('b update:' + date)
    }
}
function ObserverC(){    
    this.update = function(date){
        console.log('c update:' + date)
    }
}

运行代码:

var a = new Subject()	//目标
var b = new ObserverB()	//观察者b
var c = new ObserverC()	//观察者c

a.add(b)
a.add(c)
a.notify('change')

上图中,先通过 add 方法把观察者 b 和 c 注册到目标 a 中,在 a 中发通知时,b c 都会接收通知从而更改状态

发布/订阅模式

观察者模式很好用,但是存在一个问题,目标无法选择自己想要的消息发布,观察者会接收所有消息。

发布/订阅模式在观察者模式的基础上,在目标和观察者之间增加一个调度中心。
订阅者(观察者)把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者(目标)发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码。

观察者模式与发布/订阅模式

代码如下:

//发布者
function Public() {     
    this.handlers = {};
}
Public.prototype = {
    constructor: Public,
    on: function (eventType, handler) { // 订阅事件
        var self = this;
        if (!(eventType in self.handlers)) {
            self.handlers[eventType] = [];
        }
        self.handlers[eventType].push(handler);
        return this;
    },
    emit: function (eventType) {    // 触发事件(发布事件)
        var self = this;
        var handlerArgs = Array.prototype.slice.call(arguments, 1);
        var length = self.handlers[eventType].length
        for (var i = 0; i < length; i++) {
            self.handlers[eventType][i].apply(self, handlerArgs);
        }
        return self;
    },
    off: function (eventType, handler) {    // 删除订阅事件
        var currentEvent = this.handlers[eventType];
        var len = 0;
        if (currentEvent) {
            len = currentEvent.length;
            for (var i = len - 1; i >= 0; i--) {
                if (currentEvent[i] === handler) {
                    currentEvent.splice(i, 1);
                }
            }
        }
        return this;
    }
};
//订阅者
function ObserverB(data) {
    console.log('b subscribe:' + data)
}
function ObserverC(data) {
    console.log('c subscribe:' + data)
}

运行代码:

var publisher = new Public();

//订阅事件
publisher.on('a', ObserverB);
publisher.on('b', ObserverB);
publisher.on('a', ObserverC);

//触发事件
publisher.emit('a', 'a第1次时间发布');
publisher.emit('b', 'b第1次时间发布');
publisher.emit('a', 'a第2次时间发布'); 

订阅者 B 订阅了 a 和 b 事件,订阅者 C 订阅了 a 事件。当发布者发布 a 事件的消息时,订阅者 B C 都收到消息;当发布者发布 b 事件的消息时,只有 B 收到消息。

通过订阅者订阅不同的消息从而可以让发布者对不同人群进行消息发送。

区别

观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。所以
发布订阅模式的组件是松散耦合的。