我正在尝试学习观察者和发布者 - 订阅者模式。
来自这个简单的例子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>
&#13;
在我分享的关于观察者模式的上述链接中,它使用观察者模式jsbin
具有相同的实现我的问题在于,使用的模式不会使代码复杂化。我真的很难理解代码究竟要解决的是什么。有些人请解释一下这是什么。在jsbin代码中这是做什么。
请帮忙
由于
答案 0 :(得分:1)
不是模式方面的专家,但根据我的理解,使用简单的代码,例如单个事件监听器的示例,Observer模式肯定会有点过分。
正如您在上面的链接中所解释的那样:&#34;观察者模式是一种允许元素之间通信的简单方法,而不必依赖事件,回调或轮询。关于观察者模式的最好的事情是观察到的东西不必担心观察它的东西或它有多少观察者。&#34;它基本上允许您轻松附加观察者而无需修改基本元素代码,因为基本代码并不真正关心谁在观看它。它必须宣布它已经做了一些事情(增加了一个计数器属性),并且由观察者做出相应的反应。因此,计数器代码可以站在它自己的位置,并且没有任何依赖项运行(因此,也更容易测试)。如果您需要对观察者进行更改,您将不必触摸计数器代码并冒着引起任何副作用的风险。
相比之下,您的示例使您的回调代码和计数器彼此紧密相关。如果您需要进行类似的更改,使其具有不同的措辞或将计数器值显示在特定元素下,则您别无选择,只能触摸整个代码块。尽管如此,你的代码示例很简单,如果这就是它将要做的全部,那么使用它应该是完全没问题的。
我认为在使用异步代码和Promises之类的东西时,更容易理解Observer模式的概念,其中你的回调/观察者与你的实现异步代码分开
答案 1 :(得分:1)
首先,请确保我们在观察员模式(OP)中的术语位于同一页面:Observer
对象,Subject
(或Observee
)对象,{{1}方法和Subject.addObserver(...)
方法。
好的,现在,
没有任何模式我只能做
不,您实际上是以隐式形式使用OP。当你写道:
Subject.notify(...)
这将返回对该按钮的引用,我将其命名为container.querySelector('#click')
:
button
然后,电话var button = container.querySelector('#click');
基本上与button.addEventListener(...)
类似。这意味着您的Subject.addObserver(...)
对象实际上是OP中的button
。调用Subject
由JavaScript引擎隐式处理。使用click事件的内联函数实际上是Subject.notify(...)
。
您的代码与jarrettmeyer.com代码之间的主要区别在于:谁是Observer
?在jarrettmeyer.com中,Subject
不是任何按钮,而是一个分离的对象:Subject
对象。这提供了一些优势:
Counter
可以与多个按钮关联,例如,jarrettmeyer可以写:Subject
$("#anotherButton").on("click", function () { counter.increment(); });
可以轻松维护任何状态,并将任何信息通知Subject
。在jarrettmeyer的例子中,这些状态/信息只是一个Observer
个数字。实际上,在您的示例中,没有通知按钮的状态/信息(除了它刚刚被点击的事实),因为count
中的count
号码保留在属于实施细节的span
中。您的Observer
因此与OP无关。
答案 2 :(得分:1)
您知道您编写的代码也是观察者模式的实现吗?您在“ click”参数之后传递的函数是观察者,并将其添加到观察者的数组中。您可以针对同一元素的“ click”事件添加任意数量的函数。当'click'事件发生时,所有这些都将通过在观察者数组中运行循环来触发。
如果只有一个动作作为对其他动作的响应,则可以手动编写该动作,而无需实现观察者模式。但是,当您想在代码库的多个部分上执行某些操作以响应某个事件时,观察者模式是您的最佳选择。
答案 3 :(得分:0)
是的,你是对的。 addEventListener
或jQuery .on()
可以执行与Observer类似的操作。它们足以满足大多数前端使用的需求。但在以下用例(后端/抽象)中,观察者模式更好:
正在侦听的事件与DOM元素无关(例如JS对象的变异)
您希望对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以供参考。