js的訂閱發布者模式


  前兩天在筆試一家知名企業的時候遇到一道題,要實現一個簡單的訂閱發布者模式,當時由於各種原因我沒有做,提前交了卷。現在回想起來,還是有必要好好研究一發。

  首先先說說訂閱發布者模式,顧名思義,就是有訂閱者和發布者,兩者的功能,訂閱是請求在某些事件(event)到達時可以通知它並執行對應的動作(action),而發布則相對的是向訂閱告知事件(event)已經到達,你可以執行對應的動作(action)了。但是具體是怎么的一個思維呢,聽我娓娓道來。

  大家應該都知道nodeJs是由事件來驅動的,也就是每個函數可以說都是某個事件來觸發的,這個函數只處理這個事件對應的邏輯,函數間的通信,都是通過事件監聽來驅動。玩過游戲的人都知道,一般游戲都有一個發布消息的地方,比如我把某個怪干死之后,要對隊友們說:“看,我把怪干死了,你可以過來了”,這個其實就是一個訂閱發布者模式。首先,我是發布者,而隊友就是訂閱者,我把怪干死(這是我自己這個函數處理的邏輯),往消息框輸入消息(這就是發布),隊友們通過消息存放的地方(相當於訂閱)獲得數據,再干他們該干的事情(其他函數處理的邏輯)。懂了吧,其實說白了就是我和隊友之間夾雜着的消息存放的地方(Listener)就是訂閱發布者主要存在的地方。

  接着我們來看一下這個消息存在的地方要實現什么。第一,肯定是要把訂閱者想知道什么消息類型,也就是event,以及要執行的動作保存起來,為什么要把動作也放進來呢,明明動作是由訂閱者執行的,這就是實際思維和編程思維的不同(容我裝下逼),按照編程思維,訂閱者執行的動作是個函數,而這個函數什么時候執行完全由消息說了算,所以當然只能由Listener來執行。

  咳咳,如果以上覺得太復雜的話,我再總結一句話:訂閱者就是把要執行的函數和要的事件類型給消息存放的地方(Listener),而發布者就是告訴消息存放的地方(Listener)你現在可以執行這個事件類型對應的函數了,並且把我給你的數據也傳進去。

  好了有了思路,我們實現起來也方便多了,只要把Listener實現了就可以了,我直接上代碼。

  

(function (w) {

    var Listener = function () { // 私有變量 // 全局配置信息 var _config = { // 是否開啟多級作用域 multiLevel: true, // 發布者發布后,訂閱者相關動作是否需要刪除 removeNow: false, }, _receives = {}; // 訂閱者 // 需要傳入訂閱類型,動作 this.subscribe = function (type, action, removeNow) { // 初始化 removeNow = removeNow || _config.removeNow; // 對應的level var level = _createLevel(type); level.actions = level.actions || []; // 保證傳入的是函數 if (action instanceof Function) { level.actions.push({ action: action, removeNow: removeNow }); } }; // 發布者 // 需要傳入發布類型和數據 this.publish = function (type, data) { // 初始化 // 獲取對應actions var level = _searchLevel(type), actions = level.actions; // 遍歷執行actions里的函數 for(var i = 0, len = actions.length; i < len; i++) { actions[i].action.call(null, data); if (actions[i].removeNow) { actions.splice(i,1); } } console.log(_receives); }; // 私有函數 // 尋找執行的Level var _searchLevel = function (type) { var receives = _receives, multiLevel = _config.multiLevel; if (multiLevel) { // 有多級作用域 try { // 分割type取得各級作用域 var types = type.split('.'); for (var i = 0, len = types.length; i < len; i++) { if (receives[types[i]]) { receives = receives[types[i]]; } } return receives; } catch (e) { console.log(e); } } else { return receives[type]; } }, // 創建對應的Level _createLevel = function (type) { var receives = _receives, multiLevel = _config.multiLevel; if (multiLevel) { // 有多級作用域 try { var types = type.split('.'); for (var i = 0, len = types.length; i < len; i++) { // 有則選擇,無則初始化 receives[types[i]] = receives[types[i]] || {}; receives = receives[types[i]]; } } catch (e) { console.log(e); } } else { receives[type] = receives[type] || {}; receives = receives[type]; } return receives; }; }; w.Listener = new Listener(); console.log(w.Listener); }) (window)

使用這段代碼的方法是

<!DOCTYPE html>
<html>
<head>
    <title>test</title>
</head>
<body>
    <script src="./listener.js"></script>
    <script type="text/javascript">
        var action1 = function (data) { console.log("this is action1",data); } var person1 = Listener.subscribe('action.action1', action1, true); var action2 = function () { console.log("this is action2"); } var person2 = Listener.subscribe('action.action2', action2); Listener.publish("action.action1","hello world"); Listener.publish("action.action2"); </script> </body> </html>

  其中,我的代碼寫的是支持多級作用域的,你們可以這樣理解,因為在游戲里,你發送消息有可能是希望隊友中的某些人能看到,這里我用的"."這個符號區別作用域,千萬不要理解成js里的作用域了。當然這些代碼有些還很不完善,比如錯誤處理這些。后面我會繼續做的。

  好了,我的文筆真的是很爛,我有自知之明,而且我的代碼能力也一般。這種模式其他地方介紹得可能比我好,所以大家不喜勿看,勿噴。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM