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

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

时间: 2024-11-06 21:47:51

js的订阅发布者模式的相关文章

用订阅/发布者模式解决异步函数结果依赖的问题

我们都知道node是基于事件无阻塞i/o模型的,所以说大部分函数都是以异步实现的,请看下面代码: db.query(sql1, function (err, data) { //code }) db.query(sql2, function (err, data) { //code }) 如果我们上述两个操作,结果之间没有什么联系,那很好,基于node的I/O无阻塞模型,每个操作都做着自己的事情,美滋滋~ 但是在一些情况下这两个操作的结果有联系的,比如说第一个操作从数据库中取出一个人的姓,第二个

订阅发布者模式简单版 和 高级计数器 哈哈!

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> // 订阅发布者模式简单版 function test(){ var obj = {} var on = function(name,cal

观察者模式和订阅/发布者模式(转)

在翻阅资料的时候,有人把观察者(Observer)模式等同于发布(Publish)/订阅(Subscribe)模式,也有人认为这两种模式还是存在差异,而我认为确实是存在差异的,本质上的区别是调度的地方不同. 观察者模式 比较概念的解释是,目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口.具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法. 比如有个“天气中心”的具体目标A,专门监听天气变化,而有个显示天气的

js设计模式 —— 订阅发布模式

########################################### 因为不爱,所以都错. ########################################### 通过一个中心管理的地方,然后一个订阅中心里面的事件,当状态改变的时候,自动会通知这个组件,在另一个组件中可以通过一个方法,来触发订阅中心里面的状态的改变 原文地址:https://www.cnblogs.com/yangxueyou/p/9876389.html

JS实现观察者模式(订阅/发布模式)

实现 /*  * js 观察者模式 又称 订阅/发布模式  * 通过创建"可观察"对象,当发生一个感兴趣的事件时可将该事件通告给  * 所有观察者,从而形成松耦合 */ // 通用的发布者 EventPublisher = Base.extend({ publish: function(data, type) { EventPublisher.publish(data, type); } }, { subscribers : {         any : []    // 事件类型:

EventBus事件总线框架(发布者/订阅者模式,观察者模式)

一. android应用内消息传递的方式: 1. handler方式-----------------不同线程间传递消息. 2. Interface接口回调方式-------任意两个对象. 3. Intent进行组件间通信,广播方式. 二.单例比较好的写法: private static volatile EventBus defaultInstance; 构造函数应当是private,不应该是public 1 public static EventBus getDefault() { 2 if

AngularJS的简单订阅发布模式例子

控制器之间的交互方式广播 broadcast, 发射 emit 事件 类似于 js中的事件 , 可以自己定义事件 向上传递直到 document 在AngularJs中 向上传递直到 rootScope 观察者模式, 订阅发布模式 类似于js中的事件机制 订阅者.on('xx发布博客', function([内容]){ 通知我, 接收到博客的[内容] }) 发布者.emit('xxx发布博客', {内容}) 优点: 业务和实际触发者分离, 代码维护性相对好 缺点: 代码复杂性更高 Angular

设计模式 - 发布-订阅者模式

1.发布-订阅者 设计模式 定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知 观察者模式和发布订阅模式区别 观察者模式是由具体目标(发布者/被观察者)调度的,而发布/订阅模式是由独立的调度中心进行调度,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会:可以说发布订阅模式是观察者模式进一步解耦,在实际中被大量运用的一种模式 ** 观察者模式 ** 1.定义/解析 目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更

Publisher/Subscriber 订阅-发布模式

Publisher/Subscriber 订阅-发布模式 本博后续将陆续整理这些年做的一些预研demo,及一些前沿技术的研究,与大家共研技术,共同进步. 关于发布订阅有很多种实现方式,下面主要介绍WCF中的发布订阅,主要参考书籍<Programming WCF Services>,闲话不多说进入正题.使用传统的双工回调(例子 http://www.cnblogs.com/artech/archive/2007/03/02/661969.html)实现发布订阅模式存在许多缺陷,主要问题是,它会引