摘要:設(shè)計(jì)模式與開發(fā)實(shí)踐讀書筆記。發(fā)布訂閱模式又叫觀察者模式,它定義了對象之間的一種一對多的依賴關(guān)系。附設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式數(shù)據(jù)結(jié)構(gòu)和算法系列棧隊(duì)列優(yōu)先隊(duì)列循環(huán)隊(duì)列設(shè)計(jì)模式系列設(shè)計(jì)模式之策略模式
《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》讀書筆記。
發(fā)布-訂閱模式又叫觀察者模式,它定義了對象之間的一種一對多的依賴關(guān)系。當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變時(shí),所有依賴它的對象都將得到通知。
例如:在segmentfault我們關(guān)注了某一個(gè)問題,這個(gè)時(shí)候可以說是訂閱了這個(gè)問題的消息。當(dāng)該問題有了新的回答、評論的時(shí)候,segmentfault系統(tǒng)就會(huì)遍歷關(guān)注了這個(gè)問題的用戶,一次給用戶發(fā)消息。
現(xiàn)在看看如何一步步實(shí)現(xiàn)發(fā)布-訂閱模式。
首先,指定好發(fā)布者(如 segmentfault 的 問題系統(tǒng))
接著,給發(fā)布者添加一個(gè)緩存列表,用于存放回調(diào)函數(shù)以便通知訂閱者(問題系統(tǒng)的記錄表)
最后,當(dāng)發(fā)布者發(fā)布消息的時(shí)候,會(huì)遍歷緩存列表,依次觸發(fā)里面的回調(diào)函數(shù)(遍歷記錄表,逐個(gè)發(fā)消息)
我們還可以在回調(diào)函數(shù)里面加入一些參數(shù),訂閱者可以接收這些參數(shù),進(jìn)行各自的處理。
var sgQuestionSystem = {}; // 定義segmentfault的問題系統(tǒng) /* * 緩存列表 * clientList: { * key: [ * id:, // 唯一標(biāo)識 * fn: null // 存放回調(diào)函數(shù) * ] * } * */ sgQuestionSystem.clientList = {}; /* * 添加訂閱者(訂閱函數(shù)),將訂閱的類型與回調(diào)函數(shù)加入緩存列表 * key: 消息的類型 * id: 訂閱的唯一標(biāo)識 * fn: 訂閱的回調(diào)函數(shù) */ sgQuestionSystem.listen = function(key, id, fn) { if(!this.clientList[key]) { // 若緩存列表沒有該類型的消息,給該類消息初始化 this.clientList[key] = [] } this.clientList[key].push({ // 將訂閱的id, 回調(diào)函數(shù)添加到對應(yīng)的消息列表里 id: id, fn: fn }) } // 發(fā)布消息(發(fā)布函數(shù)), 依次通知訂閱者 sgQuestionSystem.trigger = function () { var key = Array.prototype.shift.call(arguments), // 取出消息類型 fns = this.clientList[key]; // 取出該消息對應(yīng)的回調(diào)函數(shù)集合 if(!fns || fns.length == 0) { // 若訂閱列表沒有該類型的回到函數(shù),則返回 return false; } for(var i = 0; i< fns.length; i++) { fns[i].fn.apply(this, arguments); // arguments是發(fā)布消息時(shí)附送的參數(shù),去掉了key } }
現(xiàn)在,我們來進(jìn)行一些簡單的測試:
// 張三訂閱問題A sgQuestionSystem.listen("questionA", 3, function(questionTitle, content) { console.log("張三您在早前訂閱了問題:questionA"); console.log("現(xiàn)" + questionTitle + "有了新動(dòng)態(tài)"); console.log("內(nèi)容為:" + content); }); // 李四訂閱問題A sgQuestionSystem.listen("questionB", 4, function(questionTitle, content) { console.log("李四您在早前訂閱了問題:questionB"); console.log("現(xiàn)" + questionTitle + "有了新動(dòng)態(tài)"); console.log("內(nèi)容為:" + content); }) // 問題系統(tǒng)發(fā)布消息 sgQuestionSystem.trigger("questionA", "問題A", "王五回答了問題A"); sgQuestionSystem.trigger("questionB", "問題B", "吳六回答了問題B");
至此,我們實(shí)現(xiàn)了一個(gè)簡單的發(fā)布-訂閱模式,訂閱者可以訂閱自己感興趣的事件了。
各位看官,看累了嗎? 看累的話點(diǎn)一下收藏,以便您看續(xù)集。若您還是精力充沛,那就繼續(xù)擼吧。上部分,我們實(shí)現(xiàn)了一個(gè)問題系統(tǒng)的發(fā)布-訂閱模式,現(xiàn)在,我們要實(shí)現(xiàn)一個(gè)文章的發(fā)布-訂閱模式,這時(shí)候,該怎么辦?將上面的代碼ctrl + c, ctrl + v, 再改下名字?還是有更好的解決方案?
答案顯然是有的,我們可以將發(fā)布-訂閱功能模塊提取出來,放在一個(gè)多帶帶的對象里面:
var publishSubscribeEvent = { /* * 緩存列表 * clientList: { * key: [ * id:, // 唯一標(biāo)識 * fn: null // 存放回調(diào)函數(shù) * ] * } * */ clientList: {}, /* * 添加訂閱者(訂閱函數(shù)),將訂閱的類型與回調(diào)函數(shù)加入緩存列表 * key: 消息的類型 * id: 訂閱的唯一標(biāo)識 * fn: 訂閱的回調(diào)函數(shù) */ listen: function(key, id, fn) { if(!this.clientList[key]) { this.clientList[key] = [] } this.clientList[key].push({ id: id, fn: fn }) }, // 發(fā)布消息(發(fā)布函數(shù)), 依次通知訂閱者 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); } } }
再定義一個(gè)安裝發(fā)布-訂閱的函數(shù)installPublishSubscribeEvent,這個(gè)函數(shù)可以給所有對象都動(dòng)態(tài)安裝發(fā)布-訂閱功能:
var installPublishSubscribeEvent = function(obj) { for(var i in publishSubscribeEvent) { obj[i] = publishSubscribeEvent[i]; } }
再來測試一番,我們給文章對象 sgArticleSystem 動(dòng)態(tài)添加發(fā)布-訂閱功能:
var sgArticleSystem = {}; installPublishSubscribeEvent(sgArticleSystem ); // 張三訂閱文章A動(dòng)態(tài) sgArticleSystem.listen("articleA", 3, function(articleTitle, content) { console.log("張三您在早前訂閱了文章:articleA"); console.log("現(xiàn)" + articleTitle+ "有了新動(dòng)態(tài)"); console.log("內(nèi)容為:" + content); }); // 李四訂閱文章B動(dòng)態(tài) sgArticleSystem.listen("articleB", 4, function(articleTitle, content) { console.log("李四您在早前訂閱了文章:articleB"); console.log("現(xiàn)" + articleTitle+ "有了新動(dòng)態(tài)"); console.log("內(nèi)容為:" + content); }); // 文章系統(tǒng)發(fā)布消息 sgArticleSystem.trigger("articleA", "JavaScript設(shè)計(jì)模式之發(fā)布-訂閱模式", "作者修改了文章"); sgArticleSystem.trigger("articleB", "JavaScript設(shè)計(jì)模式之策略模式", "王五用戶評論了該文章");
好了,該代碼經(jīng)過自測是沒有什么問題的,要是各位看官發(fā)現(xiàn)問題,歡迎反饋?,F(xiàn)在,我們已經(jīng)可以給我們指定的對象安裝發(fā)布-訂閱模式,但是,是不是還少了點(diǎn)什么功能呢?
答案就是少了取消訂閱事件的功能。比如張三突然不想關(guān)注該問題的更新動(dòng)態(tài)了,為了避免繼續(xù)收到問題系統(tǒng)推送過來的消息,張三需要取消之前訂閱的事件?,F(xiàn)在,我們給 publishSubscribeEvent 對象增加 remove 方法。
publishSubscribeEvent.remove = function(key, id) { var fns = this.clientList[key]; if(!fns) { // 如果key對應(yīng)的消息沒人訂閱,直接返回 return false; } if(!id) { // 如果沒傳具體的唯一標(biāo)識,則取消key的所有對應(yīng)消息 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); // 刪除訂閱者的回調(diào)函數(shù) } } } } // 測試代碼 var sgArticleSystem = {}; installPublishSubscribeEvent(sgArticleSystem ); // 張三的訂閱 sgArticleSystem.listen("articleA", 3, function(articleTitle, content) { console.log("張三您在早前訂閱了文章:articleA"); console.log("現(xiàn)" + articleTitle+ "有了新動(dòng)態(tài)"); console.log("內(nèi)容為:" + content); }); // 李四的訂閱 sgArticleSystem.listen("articleA", 4, function(articleTitle, content) { console.log("李四您在早前訂閱了文章:articleA"); console.log("現(xiàn)" + articleTitle+ "有了新動(dòng)態(tài)"); console.log("內(nèi)容為:" + content); }); sgArticleSystem.remove("articleA", 3); // 刪除張三的訂閱 sgArticleSystem.trigger("articleA", "JavaScript設(shè)計(jì)模式之發(fā)布-訂閱模式", "作者修改了文章");
上面的代碼跟原著有所不同,原著是在刪除訂閱的時(shí)候是用對比回調(diào)函數(shù)的,而我是往緩存列表加了一個(gè)唯一的標(biāo)識,用于識別。
至此,我們的發(fā)布-訂閱模式第一部分已完結(jié),歡迎大家收藏評論。
附:
JavaScript設(shè)計(jì)模式之發(fā)布-訂閱模式(觀察者模式)-Part2
JavaScript數(shù)據(jù)結(jié)構(gòu)和算法系列:
JS 棧
JS 隊(duì)列-優(yōu)先隊(duì)列、循環(huán)隊(duì)列
JavaScript設(shè)計(jì)模式系列:
JavaScript設(shè)計(jì)模式之策略模式
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/80801.html
摘要:設(shè)計(jì)模式與開發(fā)實(shí)踐讀書筆記??创宋恼虑?,建議先看設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式在中,已經(jīng)介紹了什么是發(fā)布訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布訂閱模式。 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》讀書筆記。 看此文章前,建議先看JavaScript設(shè)計(jì)模式之發(fā)布-訂閱模式(觀察者模式)-Part1 在Part1中,已經(jīng)介紹了什么是發(fā)布-訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布-訂閱模式。但是,就Part1...
摘要:設(shè)計(jì)模式與開發(fā)實(shí)踐讀書筆記??创宋恼虑?,建議先看設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式在中,已經(jīng)介紹了什么是發(fā)布訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布訂閱模式。 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》讀書筆記。 看此文章前,建議先看JavaScript設(shè)計(jì)模式之發(fā)布-訂閱模式(觀察者模式)-Part1 在Part1中,已經(jīng)介紹了什么是發(fā)布-訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布-訂閱模式。但是,就Part1...
閱讀 2572·2021-09-23 11:21
閱讀 1882·2021-09-22 15:15
閱讀 970·2021-09-10 11:27
閱讀 3440·2019-08-30 15:54
閱讀 651·2019-08-30 15:52
閱讀 1335·2019-08-30 15:44
閱讀 2349·2019-08-29 15:06
閱讀 2972·2019-08-28 18:21