I've been writing CSS3 articles for some time.
I've never written design patterns.
To write about the famous observer pattern today.
Draw a picture first
Understanding of the Observer pattern
I think it's easier to say that the publish-subscribe model is more understandable.
(but there are some books that say they are two models ...). )
It's like we subscribed to the public number on the platform.
When it has a new post, it will be pushed to all of us who subscribe
We can see the advantages of this pattern in the example
- We as subscribers do not have to see this public number every time there are no new articles published,
Public number as a Publisher will notify us at the appropriate time
- We are no longer strongly coupled with the public number. The public number doesn't care who subscribed to it,
Whether you're a male or a female or a pet dog, it only needs to be regularly posted to all subscribers
Very simple truth, when the Chinese New Year, Mass blessing SMS must be more convenient than texting
The advantages of our observer pattern are mapped out by the above example
- Can be widely used in asynchronous programming, it can replace our traditional callback function
We do not need to focus on the internal state of the object during the asynchronous execution phase, we only care about the point in time of event completion
- Instead of a hard-coded notification mechanism between objects, an object does not have to explicitly invoke the interface of another object, but loosely-coupled connections
Although we do not know the details of each other, but do not affect mutual communication. More importantly, one of the object changes does not affect the other object
I might have seen these faces, but it's okay.
Below we will take a deep look at its advantages
Custom events
In fact, the observer pattern we've all used, is the event we're familiar with.
But the built-in events often don't meet our requirements.
So we need custom events
Now we want to implement this function
Defines an event object that has the following functions
- Listen for events (subscribe to public numbers)
- Trigger event (public number release)
- Remove event (Order public number)
Of course we can't just subscribe to a public number, there may be many
So we're going to set different "keys" for different events.
So the structure of our store events should be like this.
//伪代码Event = { name1: [回调函数1,回调函数2,...], name2: [回调函数1,回调函数2,...], name3: [回调函数1,回调函数2,...],}
The code is as follows
var Event = (function(){VarList = {}, listen, trigger, remove; Listen =function(KEY,FN) {Listener Event functionsif (!List[key]) {List[key] = [];If the event list does not yet have a key value namespace, create}List[key].push (FN);Pushes the callback function into the object's "key" corresponding to the "value" callback array}; Trigger =function(){Triggering event functionsvar key =Array.prototype.shift.call (arguments);The first parameter specifies a "key" MSG =List[key];if (!msg | | msg.length = = =0) {ReturnFalseReturns False if the callback array does not exist or is empty}Forvar i =0; i < msg.length; i++) {msg[i].apply (this, arguments);Loop callback array execute callback function}}; remove =function(Key, FN) {Removing event functionsvar msg =List[key];if (!msg) {ReturnFalseEvent does not exist directly returns false}if (!fn) {Deletelist[key]; //if there are no subsequent arguments, delete the entire callback array} else{for (var i = 0; i < msg.length; i++) {if (fn = = = Msg[i]) {Msg.splice (i, 1); //Delete the callback function in the specific callback array}}}; return {listen:listen, Trigger:trigger, Remove:remove}}) (); var fn = function ' a public number ', FN); Event.trigger ( ' a public number ', ' 2016.11.26 '); Event.remove ( ' a public number ', FN);
With this global event object, we can use it to communicate between two modules
and two modules do not interfere with each other
function(){ Event.listen(...);}module2 = function(){ Event.trigger(...);}
The complete Observer object
There are still some problems with the event object we wrote above.
- You can only "subscribe" and "Publish" first
- Global objects generate naming conflicts
For the 1th, before we subscribe to a public number, it is also timed to push messages
Once we subscribe, we can also view historical push
So we should be implementing a pre-release re-subscription can still view historical messages
For the 2nd, if each of us uses the function we defined above
All events are placed in a list
It's been a long time. Naming conflicts will occur
The best way is to add the ability to create a namespace for objects
And now we're going to plump our event object
(Here I steal the code written by the great God, different people have different ways to implement the library)
Just a little bit more about this full version.
var Event = (function(){var global =This, Event, _default =' Default '; Event =function(){var _listen, _trigger, _remove, _slice =Array.prototype.slice, _shift =Array.prototype.shift, _unshift =Array.prototype.unshift, Namespacecache = {}, _create, find, each =function(ARY,FN) {VAR ret;Forvar i =0, L = ary.length; I < L; i++) {var n = ary[i]; ret = Fn.call (n,i,n); }return ret; }; _listen =function(Key,fn,cache) {if (!cache[key]) {Cache[key] = [];} cache[key].push (FN); }; _remove =function(KEY,CACHE,FN) {if (Cache[key]) {if (FN) {Forvar i = cache[key].length; I >=0; i--) {if (cache[key][i] = = = fn) {Cache[key].splice (I,1); } } }else{Cache[key] = [];}} }; _trigger =function(){var cache = _shift.call (arguments), key = _shift.call (arguments), args =arguments, _self =This, ret, stack = Cache[key];if (!stack | |!stack.length) {Return }Return each (Stack,function(){This.apply (_self,args); }); }; _create =function(namespace) {var namespace = namespace | | _default;var cache = {}, offlinestack = [],Offline event ret = {listen:function(Key,fn,last) {_listen (Key,fn,cache);if (offlinestack = = =NULL) {Return }if (last = = =' last ') {offlinestack.length && Offlinestack.pop () ();}else{each (Offlinestack,function(){This (); }); } Offlinestack =Null }, one:function(Key,fn,last) {_remove (Key,cache);This.listen (Key,fn,last); }, remove:function(KEY,FN) {_remove (KEY,CACHE,FN);}, Trigger:function(){VAR fn, args, _self =This _unshift.call (Arguments,cache); args =Arguments fn =function(){Return _trigger.apply (_self,args); };if (offlinestack) {Return Offlinestack.push (FN); }return FN (); } };Return namespace? (Namespacecache[namespace]? Namespacecache[namespace]: namespacecache[namespace] = ret): ret; };Return {create: _create, one:function(Key,fn,last) {var event =This.create (); Event.one (Key,fn,last); }, remove:function(KEY,FN) {var event =This.create (); Event.remove (KEY,FN); }, listen:function(Key,fn,last) {var event =This.create (); Event.listen (Key,fn,last); }, Trigger:function(){var event =This.create (); Event.trigger.apply (Thisarguments); } }; }();return Event;}) ();/********* first post-subscription *********/event.trigger (' Click ',1); Event.listen (' Click ',function(a) {Console.log (a);//1}); /********* uses the namespace *********/event.create (' Namespace1 '). Listen (' click ',function(a) { Console.log (a); //1}) Event.create ('namespace1 '). Trigger (' click ',1); Event.create (' Namespace3 '). Listen (' click ',function(a) {Console.log (a); //2}) Event.create ('namespace3 '). Trigger (' click ',2);
Summarize
The observer pattern has two distinct advantages
- Time decoupling
- Decoupling between objects
It is widely used, but it has its drawbacks.
Creating this function also requires memory, which makes it difficult to track maintenance
Analysis of JavaScript Design Patterns-publish-subscribe/Viewer mode