2

观察者模式 vs 发布订阅模式

在JavaScript设计模式中,有两个模式有些类似,它们就是观察者模式和发布订阅模式。有些小伙伴会觉得观察者模式就是发布订阅模式,其实它两还是有些不同的。今天就来聊聊这两基友。

首先,这两个模式都是为了维护一系列观察者,当被观察者状态发生变更时,通知这一系列观察者去进行相应更新;然而也有一些区别,那就是发布订阅模式在发布者与订阅者之间多了一个消息管理器,使得发布者与订阅者解耦。如下图所示:
观察者模式 vs 发布订阅模式
下面分别介绍下这两种模式。

观察者模式

    观察者模式:一群观察者(Observers)观察监听某个被观察对象(Subject),当有关状态发生变化时,Subject会通知这一系列Observers触发更新。

可以理解为:一个班里的学生们都在听老师讲课,当老师布置任务时,会通知学生们都去执行。

代码实现如下:

function Subject(){
    this.observers = [];
}
Subject.prototype = {
    // 添加观察者
    add: function(observer) {
        this.observers.push( observer );
    },
    // 移除观察者
    remove: function(observer) {
        var observers = this.observers;
        var len = observers.length;
        for(var i=0; i<len; i++){
            if(observers[i] === observer) {
                observers.splice(i, 1);
            }
        }
    },
    // 通知观察者
    notify: function(){
        var observers = this.observers;
        var len = observers.length;
        for(var i=0; i<len; i++){
            observers[i].update();
        }
    }
}
//观察者
function Observer(name) {
    this.name = name;
}
Observer.prototype = {
    //观察者监听到变化后要处理的逻辑
    update: function(){
        console.log('被通知了---我是观察者:', this.name);
    }
}
// 使用示例:
var subject = new Subject();
var john = new Observer('john');
var alice = new Observer('alice');
subject.add(john);
subject.add(alice);
subject.notify();  
// 最终输出结果:
// 被通知了---我是观察者: john
// 被通知了---我是观察者: alice

发布订阅模式

    发布订阅模式:一群订阅者(Subscriber)通过消息调度中心来实现基于某个主题去订阅发布者(Publisher),当有关状态发生变化时,Publisher会基于某个主题去通知该主题下对应的订阅者(Subscriber)触发更新。相比于上面的观察者模式而言,能够实现发布者与订阅者之间的解耦,而且能基于不同主题来添加订阅者,从而实现更为颗粒度的控制。

可以理解为:一个班里的学生都在听老师讲课,但是老师不止一种,可能有数学老师、语文老师、历史老师等。各门课的老师在布置任务时,学生们的作业也不同。

代码实现如下:

function PubSub(){
    this.list = {};   // 主题列表
}
PubSub.prototype = {
    // 添加订阅
    subscribe: function(key, fn){
        if(!this.list[key]) {
            this.list[key] = [];
        }
        this.list[key].push(fn);
    },
    // 取消订阅
    unSubscribe: function(key){
        delete this.list[key];
    },
    // 发布通知
    publish: function(key, para){
        if(!this.list[key]){
            alert('没有该主题---');
            return;
        }
        let arr = this.list[key];
        for(var j=0; j<arr.length; j++){
            arr[j](para);
        }
    }
}
var Pub = new PubSub();
// 为不同主题(如主题sing或者dance)添加订阅者
Pub.subscribe('sing', function(songName){
    console.log('sing 订阅者01 歌名为 ', songName)
})
Pub.subscribe('sing', function(songName){
    console.log('sing 订阅者No2 歌名为 ', songName)
})
Pub.subscribe('dance', function(para){
    console.log('dance 订阅者 歌名为 ', para)
})
// 根据不同主题(如主题sing或dance),发布不同消息
Pub.publish('sing', 'Heal the word');
Pub.publish('dance', '华尔兹舞曲');
// 最后输出结果:
// sing 订阅者01 歌名为  Heal the word
// sing 订阅者No2 歌名为  Heal the word
// dance 订阅者 歌名为  华尔兹舞曲

前端维他命
4 声望0 粉丝