【问题标题】:JavaScript Publish / Subscribe Pattern: Showing Chain of Events?JavaScript 发布/订阅模式:显示事件链?
【发布时间】:2013-11-23 06:19:29
【问题描述】:

假设我正在使用具有高度模块化代码的 pub/sub 模式。当一个模块中的函数发出“发布”时,我如何明确其他模块中的哪些函数正在订阅该触发器?

例如:

// In module 1
function foo(data) {
    publish("trigger", data);
}

// In module 2
function bar(data) {}
subscribe("trigger", bar);


// In module 3
function baz(data) {}
subscribe("trigger", baz);

在阅读模块 1 并看到“发布”已发送出去后,有人怎么知道在我的代码中查找订阅回调的位置?

一个明显的解决方案可能是注释哪些模块包含订阅触发器的函数,但在处理大量发布/订阅者时,这似乎是一个不切实际的解决方案。

我觉得我还没有完全理解如何使用 pub/sub 模式,因为对我来说,该模式似乎对函数链没有任何透明度。

编辑

我的问题是关于使我的代码清晰易懂,以便阅读我的源代码的人理解。我知道在运行时,我可以通过访问存储的回调数组以编程方式找到存储的订阅者列表。但这并没有让我的原始源代码更容易理解。

例如,我目前使用这样的模式:

// Main Controller Module
function foo(data) {
    module2.bar();
    module3.bar();
}

// In module 2
function bar(data) {}


// In module 3
function baz(data) {}

对于初学者来说,这个模块的正确术语是什么?我认为这是一个“中介者”模式,但看看here,似乎中介者模式更像我认为的 pub/sub 模式?

使用这种模式,我觉得我的代码流是完全透明的。读者无需四处寻找其他模块 foo() 可能调用的函数。

但是对于 pub/sub 模式,一旦我从 foo() 发出发布,就好像读者必须以某种方式找到订阅函数所在的模块。

当然,上述模式的缺点是严重依赖:模块 1 需要同时注入模块 2 和 3 才能调用 bar() 和 baz()。

所以我想采用 pub/sub 模式的松耦合,但我也想保持上述模式给我的功能流透明度。这可能吗?或者这只是发布/订阅模式的固有权衡?

到模组:

请删除问题。我写的这个问题很糟糕,想以更清晰的方式重新提出这个问题。谢谢。

【问题讨论】:

    标签: javascript design-patterns publish-subscribe


    【解决方案1】:

    我认为发布订阅或中介的整个想法是松散耦合对象。 Object1 不需要知道发生了什么,谁在做什么它只关心做它自己的事情并通知有兴趣的人它已经完成了它所做的事情。

    我只在控制器类中注册监听器,而不是在整个代码中。当控制器需要添加或删除侦听器时,将您的进程分解为首先通知控制器的步骤(为其创建适当的事件)。

    例如:

    1. 我们使用 XHR 获取数据。
    2. 根据我们创建处理器的数据,使用工厂创建处理器。
    3. 处理器处理数据。
    4. 显示数据。
    5. 过程结束。

    在您的控制器中,您可以:

    var Controller = {
      //fetch information and display it
      fetch : function(paramObj){
        var subscribeIds = [];
        //to have xhr listen to fetch can be done in an init function
        //  no need to add and remove every time but make a note here
        //  that it's registered in init and part of this process
        subscribeIds.push(Mediator.subscribe(xhr.fetch,"fetch"));
        //xhr will trigger dataFetched
        subscribeIds.push(Mediator.subscribe(Controller.initProsessor,"dataFetched"));
        //Controller will trigger displayFetched
        subscribeIds.push(Mediator.subscribe(dom.displayFetched,"displayFetched"));
        subscribeIds.push(Mediator.subscribe(Controller.displayedFetched,"displayedFetched"));
        paramObj.suscribeIds = subsribeIds;
        Mediator.trigger("fetch",paramObj);
      },
      initProsessor : function(paramObj){
        var processor = Processor.make(paramObj.data.type);
        paramObj.html = processor.process(data);
        Mediator.trigger("displayFetched",paramObj);
      },
      displayedFetched : function(paramObj){
        //You can decide the process is done here or take other steps
        // based on paramObj
        //You can unsubscribe listeners or leave them, when you leave them
        //  they should not be registered in the fetch function but rather
        //  in an init function of Controller with comments saying 
        //  basic fetch procedure
        Controller.cleanupListeners(paramObj.subscribeIds);
      },
      cleanupListeners : function(listenersIds){
        Mediator.unSubscribe(listenersIds);
      }
    }
    

    代码看起来比实际需要的复杂。看到它的人可能会想,为什么不让 XHR 创建一个 Processor 实例并告诉它处理呢?原因是控制器从字面上控制应用程序的流程,如果您希望在两者之间发生一些其他事情,您可以添加它们。随着应用程序的增长,您将添加越来越多的进程,有时还会重新分解函数以执行较少特定的事情,以便更好地重复使用它们。您现在只需在 Controller 中重新定义进程,而不必更改多个文件中的代码。

    所以要回答您关于在哪里可以找到侦听器以及在哪里注册事件的问题:在控制器中。

    如果您有一个 Mediator 对象,您可以让它随时转储侦听器,只需编写一个转储方法,该方法将控制台记录事件名称和函数。toString()。

    【讨论】:

    • 假设我正确理解了您的代码,这种模式可能是您的控制器需要访问您正在注册的所有监听功能。如果您使用的是 AMD 模块,则需要将它们注入您的控制器模块,从而导致一个大控制器依赖于一切。这是我目前使用的模式,但我觉得有更好的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多