javascript
JavaScript设计模式之发布-订阅模式(观察者模式)-Part1
《JavaScript設計模式與開發實踐》讀書筆記。
發布-訂閱模式又叫觀察者模式,它定義了對象之間的一種一對多的依賴關系。當一個對象的狀態發生改變時,所有依賴它的對象都將得到通知。
例如:在segmentfault我們關注了某一個問題,這個時候可以說是訂閱了這個問題的消息。當該問題有了新的回答、評論的時候,segmentfault系統就會遍歷關注了這個問題的用戶,一次給用戶發消息。
現在看看如何一步步實現發布-訂閱模式。
首先,指定好發布者(如 segmentfault 的 問題系統)
接著,給發布者添加一個緩存列表,用于存放回調函數以便通知訂閱者(問題系統的記錄表)
最后,當發布者發布消息的時候,會遍歷緩存列表,依次觸發里面的回調函數(遍歷記錄表,逐個發消息)
我們還可以在回調函數里面加入一些參數,訂閱者可以接收這些參數,進行各自的處理。
var sgQuestionSystem = {}; // 定義segmentfault的問題系統/* * 緩存列表* clientList: {* key: [* id: <int>, // 唯一標識* 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: <int>, // 唯一標識* 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設計模式之策略模式
總結
以上是生活随笔為你收集整理的JavaScript设计模式之发布-订阅模式(观察者模式)-Part1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OD逆向调试程序的笔记
- 下一篇: gradle idea java ssm