精华内容
下载资源
问答
  • 主要介绍了JavaScript设计模式观察者模式(发布订阅模式)原理与实现方法,结合实例形式分析了JavaScript观察者模式概念、原理、使用方法及相关操作注意事项,需要的朋友可以参考下
  • 发布订阅模式观察者模式

    万次阅读 多人点赞 2019-03-29 18:25:12
    设计模式并非是软件开发的专业术语,实际上,“模式”最早诞生于建筑学。 设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。通俗一点说,设计模式是在某种场合下对某个问题的一种...

    背景

    设计模式并非是软件开发的专业术语,实际上,“模式”最早诞生于建筑学。

    设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。通俗一点说,设计模式是在某种场合下对某个问题的一种解决方案。如果再通俗一点说,设计模式就是给面向对象软件开发中的一些好的设计取个名字。

    这些“好的设计”并不是谁发明的,而是早已存在于软件开发中。一个稍有经验的程序员也许在不知不觉中数次使用过这些设计模式。GoF(Gang of Four–四人组,《设计模式》几位作者)最大的功绩是把这些“好的设计”从浩瀚的面向对象世界中挑选出来,并且给予它们一个好听又好记的名字。

    设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案,他不是一个死的机制,他是一种思想,一种写代码的形式。每种语言对于各种设计模式都有他们自己的实现方式,对于某些设计模式来说,可能在某些语言下并不适用,比如工厂方法模式对于javascript。模式应该用在正确的地方。而哪些才算正确的地方,只有在我们深刻理解了模式的意图之后,再结合项目的实际场景才会知道。。

    模式的社区一直在发展。GoF在1995年提出了23种设计模式,但模式不仅仅局限于这23种,后面增加到了24种。在这20多年的时间里,也许有更多的模式已经被人发现并总结了出来,比如一些JavaScript 图书中会提到模块模式、沙箱模式等。这些“模式”能否被世人公认并流传下来,还有待时间验证。

    观察者模式(Observer Pattern)

    观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。

    观察者模式有一个别名叫“发布-订阅模式”,或者说是“订阅-发布模式”,订阅者和订阅目标是联系在一起的,当订阅目标发生改变时,逐个通知订阅者。我们可以用报纸期刊的订阅来形象的说明,当你订阅了一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸,报社和订报纸的客户就是上面文章开头所说的“一对多”的依赖关系。

    发布订阅模式(Pub-Sub Pattern)

    其实24种基本的设计模式中并没有发布订阅模式,上面也说了,他只是观察者模式的一个别称。

    但是经过时间的沉淀,似乎他已经强大了起来,已经独立于观察者模式,成为另外一种不同的设计模式。

    在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

    举一个例子,你在微博上关注了A,同时其他很多人也关注了A,那么当A发布动态的时候,微博就会为你们推送这条动态。A就是发布者,你是订阅者,微博就是调度中心,你和A是没有直接的消息往来的,全是通过微博来协调的(你的关注,A的发布动态)。

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

    我们先来看下这两个模式的实现结构:
    模式结构

    观察者模式: 观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。

    发布订阅模式: 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Topic),当发布者(Publisher)发布该事件(Publish topic)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

    我们再来看下这两个模式的代码案例:(猎人发布与订阅任务)

    观察者模式:

        //有一家猎人工会,其中每个猎人都具有发布任务(publish),订阅任务(subscribe)的功能
    	//他们都有一个订阅列表来记录谁订阅了自己
    	//定义一个猎人类
    	//包括姓名,级别,订阅列表
    	function Hunter(name, level){
    		this.name = name
    		this.level = level
    		this.list = []
    	}
    	Hunter.prototype.publish = function (money){
    		console.log(this.level + '猎人' + this.name + '寻求帮助')
    	    this.list.forEach(function(item, index){
    	    	item(money)
    	    })
    	}
    	Hunter.prototype.subscribe = function (targrt, fn){
    		console.log(this.level + '猎人' + this.name + '订阅了' + targrt.name)
    	    targrt.list.push(fn)
    	}
    	
    	//猎人工会走来了几个猎人
    	let hunterMing = new Hunter('小明', '黄金')
    	let hunterJin = new Hunter('小金', '白银')
    	let hunterZhang = new Hunter('小张', '黄金')
    	let hunterPeter = new Hunter('Peter', '青铜')
    	
    	//Peter等级较低,可能需要帮助,所以小明,小金,小张都订阅了Peter
    	hunterMing.subscribe(hunterPeter, function(money){
    		console.log('小明表示:' + (money > 200 ? '' : '暂时很忙,不能') + '给予帮助')
    	})
    	hunterJin.subscribe(hunterPeter, function(){
    		console.log('小金表示:给予帮助')
    	})
    	hunterZhang.subscribe(hunterPeter, function(){
    		console.log('小金表示:给予帮助')
    	})
    	
    	//Peter遇到困难,赏金198寻求帮助
    	hunterPeter.publish(198)
    	
    	//猎人们(观察者)关联他们感兴趣的猎人(目标对象),如Peter,当Peter有困难时,会自动通知给他们(观察者)
    

    发布订阅模式:

        //定义一家猎人工会
    	//主要功能包括任务发布大厅(topics),以及订阅任务(subscribe),发布任务(publish)
    	let HunterUnion = {
    		type: 'hunt',
    		topics: Object.create(null),
    		subscribe: function (topic, fn){
    		    if(!this.topics[topic]){
    		      	this.topics[topic] = [];  
    		    }
    		    this.topics[topic].push(fn);
    		},
    		publish: function (topic, money){
    		    if(!this.topics[topic])
    		      	return;
    		    for(let fn of this.topics[topic]){
    		    	fn(money)
    		    }
    		}
    	}
    	
    	//定义一个猎人类
    	//包括姓名,级别
    	function Hunter(name, level){
    		this.name = name
    		this.level = level
    	}
    	//猎人可在猎人工会发布订阅任务
    	Hunter.prototype.subscribe = function (topic, fn){
    		console.log(this.level + '猎人' + this.name + '订阅了狩猎' + topic + '的任务')
    	    HunterUnion.subscribe(topic, fn)
    	}
    	Hunter.prototype.publish = function (topic, money){
    		console.log(this.level + '猎人' + this.name + '发布了狩猎' + topic + '的任务')
    	    HunterUnion.publish(topic, money)
    	}
    	
    	//猎人工会走来了几个猎人
    	let hunterMing = new Hunter('小明', '黄金')
    	let hunterJin = new Hunter('小金', '白银')
    	let hunterZhang = new Hunter('小张', '黄金')
    	let hunterPeter = new Hunter('Peter', '青铜')
    	
    	//小明,小金,小张分别订阅了狩猎tiger的任务
    	hunterMing.subscribe('tiger', function(money){
    		console.log('小明表示:' + (money > 200 ? '' : '不') + '接取任务')
    	})
    	hunterJin.subscribe('tiger', function(money){
    		console.log('小金表示:接取任务')
    	})
    	hunterZhang.subscribe('tiger', function(money){
    		console.log('小张表示:接取任务')
    	})
    	//Peter订阅了狩猎sheep的任务
    	hunterPeter.subscribe('sheep', function(money){
    		console.log('Peter表示:接取任务')
    	})
    	
    	//Peter发布了狩猎tiger的任务
    	hunterPeter.publish('tiger', 198)
    	
    	//猎人们发布(发布者)或订阅(观察者/订阅者)任务都是通过猎人工会(调度中心)关联起来的,他们没有直接的交流。
    

    观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。

    观察者模式由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,这种处理方式比较直接粗暴,但是会造成代码的冗余。

    而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰,消除了发布者和订阅者之间的依赖。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。

    观察者模式是不是发布订阅模式

    网上关于这个问题的回答,出现了两极分化,有认为发布订阅模式就是观察者模式的,也有认为观察者模式和发布订阅模式是真不一样的。

    其实我不知道发布订阅模式是不是观察者模式,就像我不知道辨别模式的关键是设计意图还是设计结构(理念),虽然《JavaScript设计模式与开发实践》一书中说了分辨模式的关键是意图而不是结构

    如果以结构来分辨模式,发布订阅模式相比观察者模式多了一个中间件订阅器,所以发布订阅模式是不同于观察者模式的;如果以意图来分辨模式,他们都是实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新,那么他们就是同一种模式,发布订阅模式是在观察者模式的基础上做的优化升级。

    不过,不管他们是不是同一个设计模式,他们的实现方式确实有差别,我们在使用的时候应该根据场景来判断选择哪个。

    展开全文
  • 主要介绍了JavaScript设计模式观察者模式发布订阅模式,结合实例形式详细分析了JavaScript观察者模式发布订阅模式相关概念、原理
  • 设计模式并非是软件开发的专业术语,实际上,“模式”最早诞生于建筑学。 设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。通俗一点说,设计模式是在某种场合下对某个问题的一种...
  • 观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。观察者模式有一个别名叫“发布-订阅模式”,或者说是“订阅-发布模式”,订阅者和订阅目标是联系在一起的...

    一、观察者模式

    【1】定义

    观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。观察者模式有一个别名叫“发布-订阅模式”,或者说是“订阅-发布模式”,订阅者和订阅目标是联系在一起的,当订阅目标发生改变时,逐个通知订阅者。

    【2】案例

    案例:我办了一个补习班,学生想来我这学习,必须先报名(订阅)。收齐一帮学生,开始教学,学生们听了我的课及时更新了自己的认知。我和学生们紧密相连。每个人我都认识。我和学生就是上面文章开头所说的 "一对多" 的依赖关系。

    【3】主要解决的问题

    一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

    【4】优点

    1. 观察者和被观察者是抽象耦合的。
    2. 建立一套触发机制。

    【5】缺点

    1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
    2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
    3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

    二、发布-订阅模式

    【1】介绍

    其实24种基本的设计模式中并没有发布-订阅模式,上面也说了,他只是观察者模式的一个别称。但是经过时间的沉淀,似乎他已经强大了起来,已经独立于观察者模式,成为另外一种不同的设计模式。在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

    【2】案例

    线上课程:我在某视频网站上开了一个专栏,把我的课上传上去,喜欢的同学订阅我的专栏。后续我只要把最新课程传到视频站上就好了,学生们听了我的课亦能及时新了自己的认知。我和学生们的联系不是那么大了。我也不需要认识他们。

    后者比前者多了一个类似中转站的东西(调度中心),省了我好多事。有学生不愿意学了 ,直接找调度中心退订就好了,不用找我说。我发布的新课程也由调度中心做广播,不用我自己再一个个通知,不会影响到我自己干其他工作。

    【3】优点

    1. 支持简单的广播通信,当对象状态发生改变时,会自动通知已经订阅过的对象。
    2. 发布者与订阅者耦合性降低,发布者只管发布一条消息出去,它不关心这条消息如何被订阅者使用,同时,订阅者只监听发布者的事件名,只要发布者的事件名不变,它不管发布者如何改变
    3. 对于第一点,我们日常工作中也经常使用到,比如我们的ajax请求,请求有成功(success)和失败(error)的回调函数,我们可以订阅ajax的success和error事件。我们并不关心对象在异步运行的状态,我们只关心success的时候或者error的时候我们要做点我们自己的事情就可以了~

    【4】缺点

    1. 创建订阅者需要消耗一定的时间和内存。
    2. 虽然可以弱化对象之间的联系,如果过度使用的话,反而使代码不好理解及代码不好维护等等。

    【5】主要解决问题
    发布-订阅模式是前端常用的一种设计模式,现在主流的MVVM框架,都大量使用了此设计模式,其主要作用有以下两点:

    1. 可以实现模块间通信
    2. 可以在一定程度上实现异步编程

    前端的事件绑定有三要素:

    1. 传入事件类型
    2. 声明对应的回调方法
    3. 触发条件。触发条件为对应的事件类型。前端DOM的事件系统本质也是发布订阅模式,而我们在业务处理中所应有的模式也与此类似,只不过发布订阅模式应用的是自定义事件类型,可以自定义。

    三、两者区别

    1. 订阅-发布是观察者模式的一个变种。
    2. 观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知
    3. 订阅-发布,观察者只有订阅了才能接受到被观察者的消息,同时观察者还可以取消接受被观察者的消息,也就是说在观察者和被观察者之间存在-个经纪人Broker来管理观察者和被观察者。

    从表面上看:

    1. 观察者模式里,只有两个角色一一 观察者+被观察者
    2. 而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的一一经纪人Broker

    往更深层次讲:

    1. 观察者和被观察者,是松耦合的关系
    2. 发布者和订阅者,则完全不存在耦合

    从使用层面上讲

    1. 观察者模式,多用于单个应用内部
    2. 发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern),比如消息中间件

     

    文章每周持续更新,可以微信搜索「 前端大集锦 」第一时间阅读,回复【视频】【书籍】领取200G视频资料和30本PDF书籍资料

    展开全文
  • 观察者模式发布订阅模式的概念在项目开发中很常见 ,这里记录...通常我们所说的观察者和发布订阅模式的区别,其实只是把观察者模式的一种原始的实现方式和现在的流行的发布订阅实现方式对比)。 ​ 好了,现在先...

    观察者模式和发布订阅模式的概念在项目开发中很常见 ,这里记录一下自己的理解,详解一下两者的区别的并分别用代码实现来直观体现两者区别,便于日后温故知新

    观察者模式

    ​ 观察者模式别名也叫发布-订阅模式,但是发布订阅模式其实只是观察者模式中的一种具体的实现方式

    通常我们所说的观察者和发布订阅模式的区别,其实只是把观察者模式的一种原始的实现方式和现在的流行的发布订阅实现方式对比)

    ​ 好了,现在先在我们来理解一下原来的观察者模式是如何实现的,试讲订阅者和发布者直接关联的,将多个订阅者直接绑定到发布者身上,然后当发布者触发事件的时候吗通知各个订阅者, 是一对多的关系。

    ​ 这个我们可以举个例子,这个实现方式就类似于原始的劳动市场,应聘者是订阅者到劳动市场把简历投递到一个个公司的邮箱里,而公司相当与发布者,当有一个hc时,公司就开始一个个联系各个应聘者,完成一次发布。

    ​ 图解:(google找的)
    在这里插入图片描述
    代码:

    //观察者模式
    //招聘者
    class Subject {
        constructor() {
            this.observers = [] //观察者列表(应聘者列表)
        }
        addObserver(obj) {  //添加观察者(接受简历) 
            this.observers.push(obj)
        } 
        notify(msg) { //通知观察者 (通知应聘者来应聘)
            this.observers.forEach((e)=> {
                e.receiverNotice(msg)
            })
        }   
    }
    //应聘者
    class Observer {
        constructor(params) {
            this.name = params.name
        }
    
        receiverNotice(msg) {
            //TODO 收到消息 赶紧去面试啊!!!
        }
    }
    
    
    const  ZhangSan = new Observer({name: '张三'})
    const  LiSi = new Observer({name: '李四'})
    
    const company = new Subject()
    //绑定观察(投递简历)
    company.addObserver(ZhangSan)
    company.addObserver(LiSi)
    //发布时间 (通知)
    company.notify('快来面试啊!')
    

    订阅发布模式

    上文已经说了订阅发布模式其实只是观察者模式的一种是实现方式, 是一种升级但是现在通常被当成一种单独的设计模式。

    我们可以将订阅发布模式抽象理解成现在的劳动市场,公司和应聘者不直接接触,而由劳动市场做为事件中心做转发,应聘者向劳动市场登记自己的信息和技能,等到有公司向劳动市场发布岗位的时候, 由劳动市场来一个个通知登记了的应聘者。这与上面的观察者模式最大的区别就在于加一个事件中心(劳动中介)。

    这样公司就不需要在管理订阅者,完全交由事件中心处理。 订阅者和发布者完全解耦,发布者不需要关注怎么绑定订阅者,和如何发布给订阅者, 订阅时不需要双方都在场直接绑定 。这样能大大让代码逻辑更为清晰。同时可以将事件进行集中管理,可以进行统一的逻辑操作。

    图解:(来源google)
    在这里插入图片描述
    代码

    //event.js
    class Events {
      	//事件池
        pool = {}
        on(eventKey, fn) {
            if (!eventKey) return;
            if (!fn || typeof fn !== 'function') {
                return;
            }
            let eventPool = this.pool[eventKey] || [];
            let flag = false;
            if (eventPool.length) {
                // 相同事件重复绑定
                for (let i = 0; i < eventPool.length; i++) {
                    if (eventPool[i] === fn) {
                        flag = true;
                        break;
                    }
                }
                !flag ? eventPool.push(fn) : '';
            } else {
                eventPool.push(fn)
            }
            this.pool[eventKey] = eventPool;
        }
    
        //发布时间
        emit(eventKey) {
            let p = Promise.resolve();
            if (!eventKey) return;
            let eventPool = this.pool[eventKey] || [], args;
            if (eventPool.length) {
                args = [].splice.call(arguments, 1);
                eventPool.map(item => {
                    p = p.then(() => {
                        return itemFunc.apply(null, args);
                    }).catch(e => {
                        console.log(e)/
                    })
                })
            }
        }
                              
    		//解除事件绑定
        off(eventKey, fn) {
            let pool = this.pool;
            // 解除所有事件
            if (!eventKey) {
                this.pool = {}
                return;
            }
            // 解除所有eventKey事件
            if (!fn || typeof fn !== 'function') {
                delete pool[eventKey];
                return;
            }
    
            if (!pool[eventKey]) {
                return;
            }
    
            var mindex = pool[eventKey].findIndex((itemFunc => itemFunc === fn));
            ~mindex ? pool[eventkey].splice(mindex, 1) : '';
            this.pool = pool;
        }
    }
    const eventBus = new Events()
    
    export default eventBus;
          
          
    ---------------------
          
    // employee.js 注册事件(登记信息)
    import eventBus form "./event.js"
    eventBus.on("应聘",()=> {
      //TODO
    })
          
          
    ---------------------
    
    //compony.js 触发时间(发布一个岗位)
    import eventBus form "./event.js"
    eventBus.emit("应聘")
    

    上面就是一个发布订阅设计模式实现的事件中心类,这个类可以直接在项目中使用,

    首先这里需要知道的是在各个文件中导入eventBus时, 只会导出同一个对象,event.js文件只会执行一次 ,一个项目中每次import的只是同一个对象的指针。(具体看一下es6中import的导入方式)

    所以事件在各个文件中共享。这样就可以保证多个文件进行事件交互。因为订阅事件的时候用的是箭头函数,所以可以在外部修改作用域内的参数。这是很常用的开发场景。 类似多层的组件嵌套就可以不需要一层一层传递参数而用事件来触发

    展开全文
  • 主要介绍了JavaScript设计模式观察者模式发布者-订阅模式),本文详细的讲解了JavaScript中的观察者模式,需要的朋友可以参考下
  • 观察者模式通常的叫法叫做订阅-发布模式,类似于报刊杂志的订阅观察者和被观察者就是读者和邮局的关系,读者先要在邮局订阅想要的报刊,当报刊发行时,邮局会将报刊邮寄到读者家里。观察者(Observer)和被观察者...
  • 本文实例讲述了原生js实现的观察者和订阅模式。分享给大家供大家参考,具体如下: 观察者模式也叫 发布者-订阅模式发布发布事件,订阅者监听事件并做出反应 在传统的前端解耦方面,观察者模式作为比较常见一...
  • 发布订阅模式与观察者模式的代码实例观察者模式观察者模式的实现发布订阅模式发布订阅模式的优点发布订阅模式的缺点发布订阅模式的实现观察者模式VS发布订阅模式 观察者模式 所谓观察者模式,其实就是为了实现松耦合...

    观察者模式

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

    用《Head First设计模式》里的气象站为例子,每当气象测量数据有更新,changed()方法就会被调用,于是我们可以在changed()方法里面,更新气象仪器上的数据,比如温度、气压等等。

    但是这样写有个问题,就是如果以后我们想在changed()方法被调用时,更新更多的信息,比如说湿度,那就要去修改changed()方法的代码,这就是紧耦合的坏处。

    怎么解决呢?使用观察者模式,面向接口编程,实现松耦合。

    观察者模式里面,changed()方法所在的实例对象,就是被观察者(Subject,或者叫Observable),它只需维护一套观察者(Observer)的集合,这些Observer实现相同的接口,Subject只需要知道,通知Observer时,需要调用哪个统一方法就好了:

    在这里插入图片描述

    观察者模式的实现

    //观察者模式
    //内部基于发布订阅,收集观察者,状态变化后通知
    
    //被观察者
    class Subject{
        constructor(name){
            this.name = name
            this.state = '开心'
            this.observers = []
        }
        attach(o){
            this.observers.push(o)
        }
        setState(newState){
            this.state = newState
            this.observers.forEach(o=>o.update(this))
        }
    }
    //观察者
    class Observer{
        constructor(name){
            this.name = name
        }
        update(baby){
            console.log(`当前${this.name}被通知,小宝宝状态${baby.state}`)
        }
    }
    let baby = new Subject('宝宝')
    let father = new Observer('爸爸')
    let mother = new Observer('妈妈')
    
    // 被观察者接受观察
    baby.attach(father)
    baby.attach(mother)
    // 被观察者修改状态
    baby.setState('不开心')
    // 👇观察者被触发
    
    // 当前爸爸被通知,小宝宝状态不开心
    // 当前妈妈被通知,小宝宝状态不开心
    

    发布订阅模式

    比如小红最近在淘宝网上看上一双鞋子,但是这双鞋卖光了,于是小红订阅上货提醒,等有货的时候就会自动通知,与此同时,小明,小花等也喜欢这双鞋,也订阅上货提醒;等来货的时候就通过依次会通知他们;

    在上面的故事中,
    小红,小明等属于订阅者,订阅该商品;
    卖家属于发布者,当鞋子到了的时候,淘宝会依次通知小明,小红等;
    淘宝网属于第三者Broker(调度中心),将两个不相关的人物关联起来。

    发布订阅模式的优点

    1. 支持简单的广播通信,当对象状态发生改变时,会自动通知已经订阅过的对象。

    2. 发布者与订阅者耦合性降低,发布者只管发布一条消息出去,它不关心这条消息如何被订阅者使用,同时,订阅者只监听发布者的事件名,只要发布者的事件名不变,它不管发布者如何改变

    对于第一点,我们日常工作中也经常使用到,比如我们的ajax请求,请求有成功(success)和失败(error)的回调函数,我们可以订阅ajax的success和error事件。我们并不关心对象在异步运行的状态,我们只关心success的时候或者error的时候我们要做点我们自己的事情就可以了。

    发布订阅模式的缺点

    1. 创建订阅者需要消耗一定的时间和内存。
    2. 虽然可以弱化对象之间的联系,如果过度使用的话,反而使代码不好理解及代码不好维护等等。

    发布订阅模式的实现

    1. 首先要想好谁是发布者(比如上面的卖家)。
    2. 然后给发布者添加一个缓存列表,用于存放回调函数来通知订阅者(比如上面的买家收藏了卖家的店铺,卖家通过收藏了该店铺的一个列表名单)。
    3. 最后就是发布消息,发布者遍历这个缓存列表,依次触发里面存放的订阅者回调函数。

    实现1:

    class Center {
      constructor() {
        this.obj = {}
      }
      on(name, fn) {//订阅
        if (!Array.isArray(this.obj[name])) {
          this.obj[name] = []
        }
        this.off(name,fn)//去重
        this.obj[name].push(fn)
      }
      off(name, fn) {//取消订阅
        let tmpObj = this.obj[name]
        for (let i = 0; i < tmpObj.length; i++) {
          if (tmpObj[i] == fn) {
            tmpObj.splice(i, 1)
            break
          }
        }
      }
      emit(parmas) {//发布
        for (let name in this.obj) {
          this.obj[name].forEach((item) => {
            item(parmas)
          })
        }
      }
    }
    let a = new Center()
    // 注意要在外部传入订阅函数,否则对象地址不同,无法匹配
    let fna = (parmas) => {
      console.log('用户1可以买' + parmas)
    }
    let fnb = (parmas) => {
      console.log('用户2可以买' + parmas)
    }
    a.on('like', fna)//订阅
    a.on('like', fnb)//订阅
    a.emit('新品')//触发
    // 用户1可以买新品
    // 用户2可以买新品
    
    a.off('like', fnb)//取消
    a.emit('新品')//触发
    // 用户1可以买新品
    
    a.on('like', fna)//二次订阅,触发一次
    a.emit('新品')//触发 
    //用户1可以买新品
    
    

    实现2

    let fs = require('fs')
    
    //第三者Broker
    let event = {
        _arr:[],
        on(fn){
            this._arr.push(fn)
        },
        emit(){
            this._arr.forEach(fn=>fn())
        }
    }
    //订阅
    event.on(function(){
        console.log(Object.keys(person))
    
        if(Object.keys(person).length===3){
            console.log(person)
        }
    })
    let person = {}
    fs.readFile('./name.txt','utf8',(err,data)=>{
        console.log(data)
        person.name = data
        //发布
        event.emit()
    })
    fs.readFile('./age.txt','utf8',(err,data)=>{
        person.age = data
        event.emit()
    })
    fs.readFile('./sex.txt','utf8',(err,data)=>{
        person.sex = data
        event.emit()
    })
    

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

    大概很多人都和我一样,觉得发布订阅模式里的Publisher,就是观察者模式里的Subject,而Subscriber,就是Observer。Publisher变化时,就主动去通知Subscriber。

    其实并不是。

    在发布订阅模式里,发布者,并不会直接通知订阅者,换句话说,发布者和订阅者,彼此互不相识。

    互不相识?那他们之间如何交流?

    答案是,通过第三者,也就是在消息队列里面,我们常说的经纪人Broker。

    在这里插入图片描述

    发布者只需告诉Broker,我要发的消息,topic是AAA;
    订阅者只需告诉Broker,我要订阅topic是AAA的消息;

    于是,当Broker收到发布者发过来消息,并且topic是AAA时,就会把消息推送给订阅了topic是AAA的订阅者。当然也有可能是订阅者自己过来拉取,看具体实现。

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

    放一张极简的图,给大家对比一下这两个模式的区别:

    在这里插入图片描述

    总结

    • 从表面上看:
      观察者模式里,只有两个角色 —— 观察者 + 被观察者
      而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的 —— 经纪人Broker
    • 往更深层次讲:
      观察者和被观察者,是松耦合的关系
      发布者和订阅者,则完全不存在耦合
    • 从使用层面上讲:
      观察者模式,多用于单个应用内部
      发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern),比如我们常用的消息中间件

    参考文章:
    https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c
    https://www.cnblogs.com/tugenhua0707/p/4687947.html
    https://zhuanlan.zhihu.com/p/51357583

    展开全文
  • 发布订阅模式和观察者模式有什么不同?

    千次阅读 多人点赞 2021-04-14 23:41:40
    1. 观察者模式中,观察者和主题都知道对方的存在;而在发布订阅模式中, 生产者与消费者不知道对方的存在,它们之间通过频道进行通信。 2. 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;...
  • 改demo主要叙述java版本发布预订阅模式的主导思想(观察者模式
  • 订阅发布模式是一种特殊的生产消费模式 区别: 1.消息是否被多个对象处理。生产消费是所有消费抢占消息,订阅发布是所有订阅共享消息。 2.主动权不同。生产消费主动权在消费,订阅发布主动权在发布...
  • 观察者模式和发布订阅模式区别

    千次阅读 2020-04-27 23:00:44
    观察者模式发布订阅模式区别: ...观察者和被观察者,是松耦合的关系 发布者和订阅者,则完全不存在耦合 三、从使用层面上讲: 观察者模式,多用于单个应用内部 发布订阅模式,则更多的是一种跨应用的模式(...
  • 今天的话题是javascript中常被提及的「发布订阅模式和观察者模式」,提到这,我不由得想起了一次面试。
  • EventBus系列文章: ...在EventBus的学习当中,EventBus是基于发布订阅者模式的消息处理框架。 EventBus is an open-source library for Android and Java using the publisher/subscriber pattern for loose co...
  • 这个模式的一个重要作用就是解耦,将被观察者和观察者解耦。在很多地方,都会将观察者模式称为发布-订阅模式,或者订阅模式。实际上,两者是不完全相同的,有联系也有区别。 二、观察者模式定义 观察者模式,...
  • 以winform中parentform向childform传值为例: 降低耦合性 delegate :是一个类型...=null) { delegate1("pram")等价于 delegate1.invoke("pram") } 发布-订阅 观察者模式发布订阅的非委托实现方式 把观察者模式在优化
  • 观察者模式又称为发布/订阅(Publish/Subscribe)模式 观察者设计模式涉及到两种角色:主题(Subject)和观察者(Observer) (1)Subject模块 Subjec模块有3个主要操作 addObserver():注册添加观察者(申请订阅...
  • 注意:观察者模式和发布订阅模式是有区别的,区别在于观察者模式是由目标进行调度,而发布订阅模式是由独立的调度中心进行调度,可以说发布订阅模式观察者模式进一步解耦,在实际中被大量运用的一种模式。...
  • 从下图中可以看出,观察者模式观察者和目标直接进行交互,而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布发布了很多...
  • 发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。 整体分析 Vue 基本结构 打印 Vue 实现观察 整体结构 Vue 把 data 中的成员注入到 Vue 实例,并且把 data 中的成员转成 getter / setter...
  • 发布/订阅模式 订阅者 发布者 信号中心 我们假定,存在一个“信号中心”,某个任务执行完成,就向信号中心发布(publish)一个信号,其他任务可以向信号中心订阅(subscribe)这个信号,从而知道什么时候自己可以...
  • 1、观察者模式,又称订阅发布模式,其架构如下: 2、实例demo——订阅天气,发布天气 1&gt;WeatherSubject 目标类 public class WeatherSubject {    private List&lt;Observer&gt;...
  • 观察者模式: ObserverPattern 定义对象间的一种一对多依赖关系, 使得每当一个对象状态发生改变时, 其相关依赖对象皆得到通知并自动更新 应用 最经典的应用就是我们订阅邮件, 当有新的文章发表, 发布者会通过邮件给...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,113
精华内容 14,045
关键字:

订阅发布模式和观察者

友情链接: C-ant.rar