Overview
The observer pattern is also called publish-subscribe mode (publish/subscribe), which defines a one-to-many relationship that allows multiple observer objects to listen to a target object at the same time (for ease of understanding, the following object is called the Subscriber, the target object is called the publisher). When a publisher's status changes, all Subscribers are notified, allowing them to automatically update themselves.
The Observer pattern is used when a change in an object needs to change other objects at the same time, and it does not know how many objects need to be changed, it should consider using the observer pattern.
The central idea of the observer pattern is to promote loose coupling, one for time decoupling, and two for decoupling between objects. Let both sides of the coupling rely on abstraction, rather than relying on specifics, so that their changes will not affect the change on the other side.
Realize
JavaScript
(function(window, undefined) {var_subscribe =NULL, _publish=NULL, _unsubscribe=NULL, _shift= Array.prototype.shift,//deletes the first element of the array and returns the element_unshift = Array.prototype.unshift,//adds one or more elements at the beginning of the array and returns the array's new length valueNamespacecache ={}, _create=NULL, each=function(ary, fn) {varRET =NULL; for(vari = 0, len = ary.length; i < Len; i++) { varn =Ary[i]; RET=Fn.call (n, I, n); } returnret; }; //Subscribe to Messages_subscribe =function(Key, FN, cache) {if(!Cache[key]) {Cache[key]= []; } cache[key].push (FN); }; //Unsubscribe (Cancel all or specify messages)_unsubscribe =function(key, Cache, fn) {if(Cache[key]) {if(FN) { for(vari = cache[key].length; I >= 0; i--) { if(Cache[key][i] = = =fn) {Cache[key].splice (i,1); } } } Else{Cache[key]= []; } } }; //Publish a message_publish =function () { varCache =_shift.call (arguments), key=_shift.call (arguments), args=arguments, _self= This, ret=NULL, Stack=Cache[key]; if(!stack | |!stack.length) {return; } returnEach (Stack,function () { return This. Apply (_self, args); }); }; //Create a namespace_create =function(namespace) {varNamespace = Namespace | | "Default"; varCache ={}, Offlinestack= {},//offline events for post-release subscriptions, only onceRET ={subscribe:function(Key, FN, last) {_subscribe (key, FN, cache); if(!Offlinestack[key]) {Offlinestack[key]=NULL; return; } if(last = = = "Last") {//specifies the last function to execute the offline queue and delete after execution completesOfflinestack[key].length && Offlinestack[key].pop () ();//[].pop = Deletes the last element in an array, and returns this element}Else{each (Offlinestack[key],function () { This(); }); } Offlinestack[key]=NULL; }, one:function(Key, FN, last) {_unsubscribe (key, cache); This. Subscribe (key, FN, last); }, Unsubscribe:function(Key, FN) {_unsubscribe (key, Cache, FN); }, Publish:function () { varfn =NULL, args=NULL, Key=_shift.call (arguments), _self= This; _unshift.call (arguments, cache, key); Args=arguments; FN=function () { return_publish.apply (_self, args); }; if(Offlinestack && Offlinestack[key] = = =undefined) {Offlinestack[key]= []; returnOfflinestack[key].push (FN); } returnfn (); } }; returnNamespace? (Namespacecache[namespace]? Namespacecache[namespace]: namespacecache[namespace] =ret): ret; }; Window.pubsub={create: _create,//Create a namespaceOnefunction(Key, FN, last) {//subscribe to messages, only single-object subscriptions varPubSub = This. Create (); Pubsub.one (Key, FN, last); }, Subscribe:function(Key, FN, last) {//subscribe to messages and subscribe to multiple objects at the same time varPubSub = This. Create (); Pubsub.subscribe (Key, FN, last); }, Unsubscribe:function(Key, FN) {//Unsubscribe , (cancel all or specify messages) varPubSub = This. Create (); Pubsub.unsubscribe (key, FN); }, Publish:function() {//Publish a message varPubSub = This. Create (); Pubsub.publish.apply ( This, arguments); } };}) (window, undefined);
Application
If we are developing a mall website, the website has header, nav navigation, message list, shopping cart and other modules. The rendering of these modules has a common precondition, that is, you must first obtain the user's login information with Ajax asynchronous request.
We have no way of determining when Ajax requests will successfully return user information. More importantly, we don't know which modules need to be used in the future except header headers, nav navigation, message lists, and shopping carts. If they have strong coupling with the user information module, such as the following:
JavaScript
LOGIN.SUCC ( data) { / / Set Head of header module // Set the navigation module's avatar // Refresh message list // refresh cart list });
Now the login module is your responsibility to write, but we must also understand the header module in the method of setting the avatar is called Setavatar, Shopping Cart module Refresh method called Refresh, this coupling will make the program become stiff, The header module cannot arbitrarily change the method name of the Setavatar. This is a typical example of implementing programming, which is not approved for the implementation of specific programming.
Wait until one day, the project also added a receiving address Management module, this module is written by another colleague, at this time he will have to find you, let you log in and refresh the list of delivery address. So you turn on the login module you wrote 3 months ago, and add this line of code in the final section:
JavaScript
LOGIN.SUCC (function (data) { Header.setavatar (Data.avatar); Nav.setavatar (Data.avatar); Message.refresh (); Cart.refresh (); // Add this line of code });
We are increasingly struggling to cope with these unexpected business requirements and continually refactor the code.
After rewriting with the observer pattern, the business module that is interested in user information subscribes to the successful message event itself. When a login succeeds, the login module only needs to publish a successful login message, and when the business party receives the message, it begins to process its own business, and the login module does not care what the business party is doing, nor does it want to know its internal details. The improved code is as follows:
JavaScript
$.ajax (' http//Xxx.com?login ',function(data) {//Login SuccessfulPubsub.publish (' LOGINSUCC ', data);//Publish a successful login message}); //Each module listens for a successful login message: varHeader = (function() {//Header ModulePubsub.subscribe (' Loginsucc ',function(data) {Header.setavatar (Data.avatar); }); return{setavatar:function(data) {Console.log (' Set the head of the header module '); } };}) (); varNav = (function() {//NAV ModulePubsub.subscribe (' Loginsucc ',function(data) {Nav.setavatar (Data.avatar); }); return{setavatar:function(Avatar) {Console.log (' Avatar ' Set NAV module); } };}) ();
As mentioned above, we can change the Setavatar method name to Settouxiang at any time. If one day after the login is completed, add a refresh the receipt address list behavior, so long as in the delivery address module to add the method of listening to the message, which can be developed by the module's colleagues themselves, you as a login module developers, never care about these behaviors. The code is as follows:
JavaScript
var address = (function// address module function(obj) { Address.refresh (obj); }); return { function(avatar) { console.log (' Refresh receipt address List ');} } ;}) ();
Advantages and Disadvantages
- Supports simple broadcast communication and automatically notifies all subscribed objects;
- After page loading publishers can easily have a dynamic association with subscribers, adding flexibility;
- The abstract coupling relationship between the Publisher and the Subscriber can be extended and reused independently.
Disadvantages
- Creating the subscriber itself consumes a certain amount of time and memory, and when you subscribe to a message, perhaps the message does not finally occur, but the Subscriber will always be in memory;
- While it is possible to weaken the connection between objects, if overused, the necessary connections between objects and objects will be buried behind them, making it difficult for the program to track maintenance and understanding.
Reference
- "JavaScript design patterns and Development Practices" chapter 8th release-subscription model
- JavaScript design mode 9th Chapter 5th Section OBSERVER (Observer) mode
- Http://www.cnblogs.com/TomXu/archive/2012/03/02/2355128.html
JavaScript Design Patterns and development practices – observer patterns