【问题标题】:Why should we use Observer/Pub-Sub pattern here?为什么我们应该在这里使用观察者/发布-订阅模式?
【发布时间】:2017-01-13 00:41:30
【问题描述】:

我正在尝试学习观察者和发布者-订阅者模式。

通过这个简单的例子here

问题:有一个按钮,点击按钮应该更新计数。

没有任何模式,我可以简单地做

window.onload = function() {
  var container = document.querySelector('.container');
  var count = 0;
  container.querySelector('#click').addEventListener('click', function() {
    count = +document.querySelector("#count").innerHTML;
    count++;
    document.querySelector("#count").innerHTML = count;
  });
}
<div class="container">
  <input type="button" id="click" value="click">Total Counts: <span id="count">0</span>
</div>

在上面我分享的关于观察者模式的链接中,它有一个使用观察者模式jsbin的相同实现@

我的问题是使用模式不会使代码复杂化。我真的很难理解代码到底想解决什么问题。有人可以解释一下吗?this.notify 在 jsbin 代码中做了什么。

请帮忙

谢谢

【问题讨论】:

  • 嗯 ...simple example 声明 观察者模式是一种允许元素之间通信而无需依赖事件、回调或轮询的简单方法。 -但是您发布的 jsbin 使用事件,因此 jsbin 与简单示例无关
  • 我分享的链接有一个指向这个 jsbin 的链接...如果这不是正确的方式,你能解释一下我们如何在这里使用它
  • oops ... 抱歉,在演示页面中没有看到 onclick ... 似乎该演示页面在声明不依赖事件的代码中使用事件是自相矛盾的... . 由于演示页面本身是自相矛盾的,我不可能开始“解释”如何使用它,甚至作者都无法做到!
  • 好的,如果是这样的话,你能举一个观察者模式使用的例子吗,这对我理解真的很有帮助
  • 我刚刚解释了,那个观察者模式页面是自相矛盾的,我不能假设它知道作者的意图

标签: javascript jquery design-patterns publish-subscribe observer-pattern


【解决方案1】:

不是模式专家,但据我了解,使用像您的示例这样的简单代码接受单个事件侦听器,观察者模式肯定是矫枉过正。

正如您在上面的链接中所解释的:“观察者模式是一种允许元素之间通信而不必依赖事件、回调或轮询的简单方法。观察者模式最好的一点是被观察的事物不会必须担心是什么在观察它,或者它有多少观察者。”它基本上允许您轻松附加观察者,而无需修改基本元素代码,因为基本代码并不真正需要关心谁在看它。它只需要宣布它已经做了某事(增加了一个计数器属性),并由观察者做出相应的反应。正因为如此,计数器代码可以独立存在并且没有任何依赖项可以运行(因此也更容易测试)。如果您需要对观察者进行更改,则无需触碰计数器代码,也不必冒任何副作用的风险。

相比之下,您的示例的回调代码和计数器彼此紧密相关。如果您需要进行更改,例如使其具有不同的措辞或让计数器值出现在特定元素下,您别无选择,只能触及整个代码块。同样,您的代码示例很简单,如果这就是它要做的全部,那么它应该可以很好地使用。

我认为在处理异步代码和 Promises 之类的东西时,更容易理解观察者模式的概念,其中你的回调/观察者与你实现的异步代码是分开的

【讨论】:

    【解决方案2】:

    首先,请确保我们在观察者模式 (OP) 中的术语是一致的:Observer 对象、Subject(或Observee)对象、Subject.addObserver(...) 方法和Subject.notify(...) 方法.

    好的,现在,

    没有任何模式,我可以简单地做

    不,您实际上是以隐式形式使用 OP。当你写的时候:

    container.querySelector('#click')
    

    这将返回对按钮的引用,我将其命名为button

    var button = container.querySelector('#click');
    

    那么调用button.addEventListener(...) 基本上类似于Subject.addObserver(...)。这意味着您的 button 对象实际上是 OP 中的 Subject。调用 Subject.notify(...) 由 JavaScript 引擎隐式处理。而你消耗点击事件的内联函数实际上是Observer

    您的代码与 jarrettmeyer.com 的代码的主要区别在于:Subject 是谁?在 jarrettmeyer.com 中,Subject 不是任何按钮,而是一个单独的对象:Counter 对象。这提供了一些优势:

    1. Subject可以关联很多按钮,例如jarrettmeyer可以写成:$("#anotherButton").on("click", function () { counter.increment(); });

    2. Subject 可以轻松保持任何状态并将任何信息通知给Observer。在 jarrettmeyer 的示例中,这些状态/信息只是一个 count 数字。实际上,在您的示例中,没有通知按钮的状态/信息(除了刚刚单击它的事实),因为 count 编号保留在您的 span 中,它属于您的 Observer 的实现细节因此与 OP 无关。

    【讨论】:

      【解决方案3】:

      你知道你写的代码也是观察者模式的实现吗?你在“click”参数之后传递的函数是一个观察者,它被添加到观察者的数组中。您可以针对同一元素的“点击”事件添加任意数量的函数。当 'click' 事件发生时,它们都将通过在观察者的数组中运行一个循环来触发。

      如果您只有一个动作作为对其他动作的响应而发生,您可以手动编写动作而无需实现观察者模式。但是,当您想在代码库的多个部分执行多项操作以响应某个事件时,观察者模式是可行的方法。

      【讨论】:

        【解决方案4】:

        是的,你是对的。 addEventListener 或 jQuery .on() 可以做与 Observer 类似的事情。它们对于大多数前端使用来说已经足够好了。但在以下用例(后端/抽象)中,观察者模式更好:

        1. 被监听的事件与 DOM 元素无关(例如 JS 对象的突变)

        2. 您希望更好地控制removeEventListener(例如,多个匿名回调函数绑定在一个事件类型上,您希望移动其中一个)

        示例中的.notify方法用于循环注册表数组中的所有回调函数,并尝试全部执行。

        这是一个Codepen,展示观察者如何在现实世界中提供帮助。

        当我学习观察者模式时,这是一个简单的观察者实现:

        var App = function() {
          // This array will store all the subscribers.
          this.subscribers = [];
        }
        // Subscribe, unsubscribe and publish are three base methods in this pattern
        App.prototype.subscribe = function(subscriber) {
          this.subscribers.push(subscriber);
        }
        App.prototype.unsubscribe = function(subscriber) {
          for (var i = 0; i < this.subscribers.length; i++) {
            if (this.subscribers[i] === subscriber) {
              this.subscribers.splice(i, 1);
            }
          }
        }
        App.prototype.publish = function(message) {
          for (var i = 0; i < this.subscribers.length; i++) {
            console.log(this.subscribers[i] + ' got ' + message + '!');
          }
        }
        // Testing code.
        var myApp = new App();
        myApp.subscribe('Timmy');
        myApp.subscribe('Tommy');
        myApp.publish('a new magazine'); // Both Timmy & Tommy got the new magazine
        myApp.unsubscribe('Timmy');
        myApp.publish('a new book'); // Now only Tommy got the new book
        

        附上Codepen供参考。

        【讨论】:

          猜你喜欢
          • 2019-11-09
          • 2012-05-07
          • 1970-01-01
          • 2017-09-17
          • 1970-01-01
          • 1970-01-01
          • 2021-12-26
          • 1970-01-01
          • 2015-02-24
          相关资源
          最近更新 更多