@thwonghenry refactor the javascript of an inhouse plugin and I am the one who review his code. He apply the observer pattern and we have some discussion on it.
Observer pattern
There are two roles in observer pattern, subject and observer. Subject is an object implemented on()
and notify()
. Observers are modules, objects or functions depend on subject.
The pattern works in this way: observers’ logic is not embedded in subject directly, they register a “handler” to subject’s “events” using on()
. When the subject is called, subject fire an event using notify()
and call the “handlers” which registered that specific event.
When and why we use observer pattern?
From the definition, we know there 3 components in observer pattern: subject, events and observers. A specific action of subject trigger specific events and specific observers are notified by specific event. In other words, specific subject action trigger specific observer action indirectly. Subject is depending on observers. Observer pattern is decoupling the subject and observers by converting the dependency to an “event”.
As long as two objects or modules have a fixed dependency or relationship, we can apply observer pattern. But why we need it?
Reuse similar dependency
It is common to have dependency among modules, we can convert an abstract “dependency” to a concrete “event”. Therefore we can organize the dependency in a tidy way. Also, when there are new dependency, we can register the function to the “event” without changing logic of any modules.
For example, when there is a fire in building, we need to call fire station, turn on sprinkler and resident need to escape. We can consider resident
, sprinkler
and fireStation
objects depend on building
object. We can implement the dependency like this:1
2
3
4
5building.fire = function(){
resident.escape();
sprinkler.water();
fireStation.call();
}
If we need to do something more now, such as journalist needs to report the fire in news, we have to update building.fire
.1
2
3
4
5
6
7building.fire = function(){
building.notify("fire");
resident.escape();
sprinkler.water();
fireStation.call();
journalist.report();
}
Everytime we change a piece of deployed code, we need to do a lot of testing to make sure no new or regression bugs is introduced. This can be avoided by observer pattern.
1 | building.fire = function(){ |
To let journalist to report the new, we only need to append building.on("fire", journalist.report);
to some where, without changing any implemented functions.
How observer pattern plays its part in MVC?
In user interface, there are two main components, data models and front end views. They have a fixed dependency. For example, when user click a button, some data need to be updated. After a model is updated, some tables need to be updated. The code will look like this:1
2
3
4
5
6
7
8
9
10
11button.click = function(){
data.update();
...
}
data.update = function(){
table.update();
...
}
table.update = function(){
...
}
We have a button.click -> data.update -> tables.update chains. When we have more UI components or more complex data logic, the chain will be getting longer and the code become unmaintainable.
If we use observer pattern, the code will look like this1
2
3
4
5
6
7
8
9
10button.click = function(){
this.notify("click");
}
data.update = function(){
...
this.notify("update");
}
table.update = function(){
...
}
and we have to register handlers to events.1
2button.on('click', data.update);
data.on('update', table.update);
There are no nested function calls anymore. Observer “flatten” the code for us. Observer pattern is converting “model update” and “view update” dependencies into events and save programmers from trivial handling of components update chains.
We have a MVC structure now. We can put data.update()
in models, buton.click()
and table.update()
in views. The controller will be the event registration logic like button.on('click', data.update);
and data.on('update', table.update);
.
MVC is an example of observer pattern
To sum up, MVC is a special scenario of observer pattern, which hide the detail on update chains of models and views. By putting the dependency definition in controller, we can decouple model logic and view logic.
Further improvement
After we identitfy the problem in MVC, we will get redux. But I am not familiar with it, I better not discuss it here.
Reference
http://stackoverflow.com/questions/15594905/difference-between-observer-pub-sub-and-data-binding
https://en.wikipedia.org/wiki/Observer_pattern