简体   繁体   中英

Why should we use Observer/Pub-Sub pattern here?

I am trying to learn Observer and publisher-subscriber pattern.

came through this simple example here

Problem: There is a button and onclick of the button it should be updating the count.

without any pattern i can do simply as

 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> 

In the above link that i have shared about observer pattern it has an implementation for the same using observer pattern jsbin

My Question here, is the usage of a pattern not complicating the code. I am really having a bad time of understanding what exactly the code is trying to solve .can some one please explain this and what is this.notify doing in the jsbin code.

Please help

Thanks

Not an expert in patterns but from what I understand, with simple code like your example that takes in a single event listener, the Observer Pattern would definitely be overkill.

As explained in your link above: "The observer pattern is a simple way to allow communication between elements without having to rely on events, callbacks, or polling. The best thing about the observer pattern is that the thing being observed does not have to worry about what is observing it or how many observers it has." It basically allows you to attach observers easily without having to modify the base element code, because the base code doesn't really have to care about who is watching it. It just has to announce that it's done something (increased a counter property) and it's up to the observers to react accordingly. Because of this, the counter code could stand on it's own and not have any dependencies to run (thus, making it easier to test as well). If you need to make changes to your observers, you won't have to touch the counter code and risk causing any side effects.

In comparison, your example has your callback code and counter heavily tied to one another. If you need to make a change like say, making it have different wording or have the counter value appear under a specific element, you have no choice but to touch that entire block of code. Again though, your code example is simple enough and if that is all it will be doing, then it should be perfectly fine to use.

I think it's easier to understand the concept of the Observer pattern when working with stuff like async code and Promises, where your callbacks/observers become separate from your implementing async code

Firstly, please make sure we are on the same page regarding the terminologies in Observer Pattern (OP): Observer object, Subject (or Observee ) object, Subject.addObserver(...) method, and Subject.notify(...) method.

OK, now,

without any pattern i can do simply as

No, you are actually using OP in an implicit form. When you wrote:

container.querySelector('#click')

This will return a reference to the button, I name it button :

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

Then the call button.addEventListener(...) is basically an analogy to Subject.addObserver(...) . This means that your button object is actually the Subject in OP. The call Subject.notify(...) is implicitly handled by the JavaScript engine. And your inline function to consume the click event is actually the Observer .

The main difference between your code and the code of jarrettmeyer.com lies in the question: who is the Subject ? In jarrettmeyer.com, Subject is not any button but a separated object: the Counter object. This offers some advantages:

  1. The Subject can associate with many buttons, for example, jarrettmeyer can write: $("#anotherButton").on("click", function () { counter.increment(); });

  2. The Subject can easily maintain whatever state and notify whatever info to the Observer . In jarrettmeyer's example, these state/info are simply a count number. Indeed, in your example, no state/info of the button (except the fact that it has just been clicked) is notified since the count number is maintained in your span which belongs to the implementation detail of your Observer and thus not related to OP.

Do you know the code you wrote is also an implementation of the observer pattern? The function you passed after the 'click' argument is an observer and it is added to the observers' array. You can add as many functions as you want against the 'click' event of the same element. They all will be fired by running a loop in the observers' array when the 'click' event happens.

If you have only one action happening as a response to some other action, you can write the action manually without implementing the observer pattern. However, when you want to do multiple things at multiple parts of the codebase in response to some event, observer pattern is the way to go.

Yes, you are right. addEventListener or jQuery .on() could do the similar thing as Observer. They are good enough for most of the front-end usage. But in the following use cases (backend/abstraction), observer pattern is better:

  1. The event being listened is not related to the DOM elements (eg JS object's mutation)

  2. You would like to have a better control on removeEventListener (eg multiple anonymous callback functions bound on an event type, you would like to move one of them)

The .notify method in the example is made to loop all the callback function in registry array, and try to execute all of them.

Here's a Codepen to show how observer help in the real world.

And here's a simple observer implementation when I learn Observer pattern:

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

Attached the Codepen for reference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM