zoukankan      html  css  js  c++  java
  • 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里的作用域了。当然这些代码有些还很不完善,比如错误处理这些。后面我会继续做的。

      好了,我的文笔真的是很烂,我有自知之明,而且我的代码能力也一般。这种模式其他地方介绍得可能比我好,所以大家不喜勿看,勿喷。

  • 相关阅读:
    [大山中学模拟赛] 2016.9.17
    [DP优化方法之斜率DP]
    Gengxin讲STL系列——String
    小班讲课之动态规划基础背包问题
    ubuntu安装体验
    小班出题之字符串基础检测
    G
    B
    小项目--反eclass
    树--天平问题
  • 原文地址:https://www.cnblogs.com/empty-run/p/5838477.html
Copyright © 2011-2022 走看看