摘要:设计模式与开发实践读书笔记。发布订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系。附设计模式之发布订阅模式观察者模式数据结构和算法系列栈队列优先队列循环队列设计模式系列设计模式之策略模式
《JavaScript设计模式与开发实践》读书笔记。
发布-订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。
例如:在segmentfault我们关注了某一个问题,这个时候可以说是订阅了这个问题的消息。当该问题有了新的回答、评论的时候,segmentfault系统就会遍历关注了这个问题的用户,一次给用户发消息。
现在看看如何一步步实现发布-订阅模式。
首先,指定好发布者(如 segmentfault 的 问题系统)
接着,给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(问题系统的记录表)
最后,当发布者发布消息的时候,会遍历缓存列表,依次触发里面的回调函数(遍历记录表,逐个发消息)
我们还可以在回调函数里面加入一些参数,订阅者可以接收这些参数,进行各自的处理。
var sgQuestionSystem = {}; // 定义segmentfault的问题系统 /* * 缓存列表 * clientList: { * key: [ * id:, // 唯一标识 * fn: null // 存放回调函数 * ] * } * */ sgQuestionSystem.clientList = {}; /* * 添加订阅者(订阅函数),将订阅的类型与回调函数加入缓存列表 * key: 消息的类型 * id: 订阅的唯一标识 * fn: 订阅的回调函数 */ sgQuestionSystem.listen = function(key, id, fn) { if(!this.clientList[key]) { // 若缓存列表没有该类型的消息,给该类消息初始化 this.clientList[key] = [] } this.clientList[key].push({ // 将订阅的id, 回调函数添加到对应的消息列表里 id: id, fn: fn }) } // 发布消息(发布函数), 依次通知订阅者 sgQuestionSystem.trigger = function () { var key = Array.prototype.shift.call(arguments), // 取出消息类型 fns = this.clientList[key]; // 取出该消息对应的回调函数集合 if(!fns || fns.length == 0) { // 若订阅列表没有该类型的回到函数,则返回 return false; } for(var i = 0; i< fns.length; i++) { fns[i].fn.apply(this, arguments); // arguments是发布消息时附送的参数,去掉了key } }
现在,我们来进行一些简单的测试:
// 张三订阅问题A sgQuestionSystem.listen("questionA", 3, function(questionTitle, content) { console.log("张三您在早前订阅了问题:questionA"); console.log("现" + questionTitle + "有了新动态"); console.log("内容为:" + content); }); // 李四订阅问题A sgQuestionSystem.listen("questionB", 4, function(questionTitle, content) { console.log("李四您在早前订阅了问题:questionB"); console.log("现" + questionTitle + "有了新动态"); console.log("内容为:" + content); }) // 问题系统发布消息 sgQuestionSystem.trigger("questionA", "问题A", "王五回答了问题A"); sgQuestionSystem.trigger("questionB", "问题B", "吴六回答了问题B");
至此,我们实现了一个简单的发布-订阅模式,订阅者可以订阅自己感兴趣的事件了。
各位看官,看累了吗? 看累的话点一下收藏,以便您看续集。若您还是精力充沛,那就继续撸吧。上部分,我们实现了一个问题系统的发布-订阅模式,现在,我们要实现一个文章的发布-订阅模式,这时候,该怎么办?将上面的代码ctrl + c, ctrl + v, 再改下名字?还是有更好的解决方案?
答案显然是有的,我们可以将发布-订阅功能模块提取出来,放在一个多带带的对象里面:
var publishSubscribeEvent = { /* * 缓存列表 * clientList: { * key: [ * id:, // 唯一标识 * fn: null // 存放回调函数 * ] * } * */ clientList: {}, /* * 添加订阅者(订阅函数),将订阅的类型与回调函数加入缓存列表 * key: 消息的类型 * id: 订阅的唯一标识 * fn: 订阅的回调函数 */ listen: function(key, id, fn) { if(!this.clientList[key]) { this.clientList[key] = [] } this.clientList[key].push({ id: id, fn: fn }) }, // 发布消息(发布函数), 依次通知订阅者 trigger: function () { var key = Array.prototype.shift.call(arguments), fns = this.clientList[key]; if(!fns || fns.length == 0) { return false; } for(var i = 0; i< fns.length; i++) { fns[i].fn.apply(this, arguments); } } }
再定义一个安装发布-订阅的函数installPublishSubscribeEvent,这个函数可以给所有对象都动态安装发布-订阅功能:
var installPublishSubscribeEvent = function(obj) { for(var i in publishSubscribeEvent) { obj[i] = publishSubscribeEvent[i]; } }
再来测试一番,我们给文章对象 sgArticleSystem 动态添加发布-订阅功能:
var sgArticleSystem = {}; installPublishSubscribeEvent(sgArticleSystem ); // 张三订阅文章A动态 sgArticleSystem.listen("articleA", 3, function(articleTitle, content) { console.log("张三您在早前订阅了文章:articleA"); console.log("现" + articleTitle+ "有了新动态"); console.log("内容为:" + content); }); // 李四订阅文章B动态 sgArticleSystem.listen("articleB", 4, function(articleTitle, content) { console.log("李四您在早前订阅了文章:articleB"); console.log("现" + articleTitle+ "有了新动态"); console.log("内容为:" + content); }); // 文章系统发布消息 sgArticleSystem.trigger("articleA", "JavaScript设计模式之发布-订阅模式", "作者修改了文章"); sgArticleSystem.trigger("articleB", "JavaScript设计模式之策略模式", "王五用户评论了该文章");
好了,该代码经过自测是没有什么问题的,要是各位看官发现问题,欢迎反馈。现在,我们已经可以给我们指定的对象安装发布-订阅模式,但是,是不是还少了点什么功能呢?
答案就是少了取消订阅事件的功能。比如张三突然不想关注该问题的更新动态了,为了避免继续收到问题系统推送过来的消息,张三需要取消之前订阅的事件。现在,我们给 publishSubscribeEvent 对象增加 remove 方法。
publishSubscribeEvent.remove = function(key, id) { var fns = this.clientList[key]; if(!fns) { // 如果key对应的消息没人订阅,直接返回 return false; } if(!id) { // 如果没传具体的唯一标识,则取消key的所有对应消息 fns && (fns.length = 0); } else { for(var l = fns.length - 1; l >=0; l--) { var _id = fns[l].id; if(_id == id) { fns.splice(l, 1); // 删除订阅者的回调函数 } } } } // 测试代码 var sgArticleSystem = {}; installPublishSubscribeEvent(sgArticleSystem ); // 张三的订阅 sgArticleSystem.listen("articleA", 3, function(articleTitle, content) { console.log("张三您在早前订阅了文章:articleA"); console.log("现" + articleTitle+ "有了新动态"); console.log("内容为:" + content); }); // 李四的订阅 sgArticleSystem.listen("articleA", 4, function(articleTitle, content) { console.log("李四您在早前订阅了文章:articleA"); console.log("现" + articleTitle+ "有了新动态"); console.log("内容为:" + content); }); sgArticleSystem.remove("articleA", 3); // 删除张三的订阅 sgArticleSystem.trigger("articleA", "JavaScript设计模式之发布-订阅模式", "作者修改了文章");
上面的代码跟原著有所不同,原著是在删除订阅的时候是用对比回调函数的,而我是往缓存列表加了一个唯一的标识,用于识别。
至此,我们的发布-订阅模式第一部分已完结,欢迎大家收藏评论。
附:
JavaScript设计模式之发布-订阅模式(观察者模式)-Part2
JavaScript数据结构和算法系列:
JS 栈
JS 队列-优先队列、循环队列
JavaScript设计模式系列:
JavaScript设计模式之策略模式
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80801.html
摘要:设计模式与开发实践读书笔记。看此文章前,建议先看设计模式之发布订阅模式观察者模式在中,已经介绍了什么是发布订阅模式,同时,也实现了发布订阅模式。 《JavaScript设计模式与开发实践》读书笔记。 看此文章前,建议先看JavaScript设计模式之发布-订阅模式(观察者模式)-Part1 在Part1中,已经介绍了什么是发布-订阅模式,同时,也实现了发布-订阅模式。但是,就Part1...
摘要:设计模式与开发实践读书笔记。看此文章前,建议先看设计模式之发布订阅模式观察者模式在中,已经介绍了什么是发布订阅模式,同时,也实现了发布订阅模式。 《JavaScript设计模式与开发实践》读书笔记。 看此文章前,建议先看JavaScript设计模式之发布-订阅模式(观察者模式)-Part1 在Part1中,已经介绍了什么是发布-订阅模式,同时,也实现了发布-订阅模式。但是,就Part1...
阅读 2441·2021-09-23 11:21
阅读 1851·2021-09-22 15:15
阅读 933·2021-09-10 11:27
阅读 3406·2019-08-30 15:54
阅读 619·2019-08-30 15:52
阅读 1301·2019-08-30 15:44
阅读 2315·2019-08-29 15:06
阅读 2934·2019-08-28 18:21