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

观察者模式,和发布订阅模式,有什么区别?

你可能听过这二者是一样的,没有差别。但是二者其实是不一样的,属于两种不同模式。

观察者模式

所谓观察者模式,其实就是为了实现松耦合。

举例:以一个小宝宝为例,每当小宝宝的心情发生变化,changed() 方法被调用,然后我们在 changed() 方法里面,更新宝宝的心情。

假如我们要在小宝宝心情发生变化之后,增加新的操作,更新宝宝的其他特征,的这时候我们就需要去修改changed()方法的代码,这就是紧耦合的坏处。

观察者模式里面, changed()方法所在的实例对象,就是被观察者(小宝宝),它只需维护一套观察者(爸爸、妈妈、...其他关心小宝宝的)。这些(爸爸、妈妈、...其他关心小宝宝的)实现相同的接口(关系小宝宝的变化),宝宝只需要,通知爸爸妈妈去调用拿个统一的方法就好了。

JavaScript 实现观察者模式


// 被观察者:我家小宝宝,心情好不好
// 观察者: 爸爸、妈妈
// 心情好 -> 心情不好
class Subject {
    constructor (name) {
        this.name = name;
        this.observers = [];
        this.state = '心情很美丽';
    }
    // 收集观察者
    attach (observer) {
        this.observers.push(observer); 
    }
    // 改变小宝宝心情
    setState (newState) { 
        this.state = newState;
        this.observers.forEach(o => o.update(newState));
    }
}

class Observer {
    constructor (name) {
        this.name = name;
    }
    // 用来通知所有的观察者 小宝宝心情变化了
    update (newState) {
        console.log(this.name, newState);
    }
}



let sub = new Subject('小宝宝');

let o1 = new Observer('爸爸');
let o2 = new Observer('妈妈');

sub.attach(o1);
sub.attach(o2);

sub.setState('心情不好');

Vue 中在实现双向绑定中也使用了观察者模式。请查看另一篇  实现简易版的 Vue 。

发布订阅模式

很让人误会的就是 发布 和 "小宝宝" 的工作是一样的,都是用来进行集体通知的。

这点很相同的。

但是不同的是,是否是直接通知的一个问题。"小宝宝" 是根据自己的变化去主动通知爸爸妈妈的,而发布 / 订阅不是直接产生关系的。它们是通过第三者去管理的。

举例:以买奶粉为例,通过网上客服订购,由客服来记录所有需要订购的用户,并且由客服来奶粉公司去购买奶粉。

也就是说,发布订阅模式里,发布者和订阅者,不是松耦合,而是完全解耦的。

JavaScript 实现发布订阅

function Events() {
    this.topics = {};
}

Events.prototype.subscribe = function (topic, handler) {
    if (!this.topics.hasOwnProperty(topic)) {
        this.topics[topic] = [];
    }
    
    this.topics[topic].push(handler);
}

Events.prototype.publish = function (topic, message) {
    if (this.topics.hasOwnProperty(topic)) {
        this.topics[topic].forEach(function (handler) {
            handler(message ? message: {});
        })
    }
}

Events.prototype.remove = function (topic, handler) {
    if (!this.topics.hasOwnProperty(topic)) {
        return;
    }
    const _that = this;
    let handlerIndex = -1;
    this.topics[topic].forEach(function (item, index) {
        if (item === handler) {
            handlerIndex = index;
            _that.topics[topic].splice(index, 1);
        }
    })
}

Events.prototype.removeAll = function (topic) {
    if (this.topics.hasOwnProperty(topic)) {
        this.topics[topic].length = 0;
    }
    
}

// test
const events = new Events();

function handler1(info) {
    console.log('handler1', info);
}

events.subscribe('click', handler1);

events.publish('click', 'hello world');

events.remove('click', handler1);

实现 Node.JS 中的 event

class EventEmitter {
    constructor() {
        this.events = {};
    }

    on(event, callback) {
        if (!this.events.hasOwnProperty(event)) {
            this.events[event] = [];
        }
        this.events[event].push(callback);
        return this;
    }

    emit(event, ...args) {
        if (this.events.hasOwnProperty(event)) {
            this.events[event].forEach(callback => {
                callback(...args);
            })
        }
        return this;
    }

    off(event, callback) {
        if (!this.events.hasOwnProperty(event)) {
            return;
        }
        let callbacks = this.events[event]
        this.events[event] = callbacks.filter(fn => fn !== callback);
        return this; 
    }

    once(event, callback) {
        let wrapFun = (...args) => {
            callback(...args);
            this.off(event, wrapFun);
        };

        this.on(event, wrapFun);
        return this;
    }
}


const events = new EventEmitter();
function handler(info, info1) {
    console.log(info, info1);
}
events.on('click', handler);
events.emit('click', 'hello world', 'hello bai');
events.off('click', handler);

events.once('hand', (a, b, c) => {
    console.log(a, b, c);
})
events.emit('hand', '小白', '小黑', '小紫');

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值