why?
在一个事件驱动的环境里面,Observer Pattern 是一个非常好的工具,它可以用来管理对象、处理事件、状态。
what ?
Observer Pattern 能让对象观察别的对象,当别的对象发生变化时,这个对象能够被通知
这里涉及到两个概念:
observer :也即subscribers,可以subscribe ,也可以unsubscribe,同时也可以消费数据
observed:也即publishers,生产,可以giving ,也可以 being taken from (即可以发送,也可以等subscriber 来自己拿)
案例1 Newspaper Delivery
在报纸事业中,
用户就是subscribers,他们接到报纸后,消费数据(报纸)
出版社就是publishers,他们生产报纸,并传递数据(报纸)
push vs pull
有个问题:对于出版社,为了少许的几个用户,环游世界送报纸,显然不合实际。同时,对于用户,跑得老远去拿定的报纸,也不合实际。
这里有两种方法,可以让用户得到报纸,push and pull
push ,出版社分发报纸
pull,用户去拿报纸
pull 机制
var Publisher = new Observable; var Subscriber = function(news) { // news delivered directly to my front porch }; Publisher.subscribeCustomer(Subscriber); Publisher.deliver('extre, extre, read all about it'); Publisher.unSubscribeCustomer(Subscriber);
这个模型中,publisher 是核心,他们注册用户,传递报纸等
换一种思考方式,处理上面的情景
var NewYorkTimes = new Publisher; var AustinHerald = new Publisher; var SfChronicle = new Publisher;//三种报纸出版社 var Joe = function(from) { //三个用户 console.log('Delivery from '+from+' to Joe'); }; var Lindsay = function(from) { console.log('Delivery from '+from+' to Lindsay'); }; var Quadaras = function(from) { console.log('Delivery from '+from+' to Quadaras'); }; Joe.subscribe(NewYorkTimes).subscribe(SfChronicle); Lindsay.subscribe(AustinHerald).subscribe(SfChronicle).subscribe(NewYorkTimes); Quadaras.subscribe(AustinHerald).subscribe(SfChronicle); NewYorkTimes.deliver('Here is your paper! Direct from the Big apple'); AustinHerald.deliver('News').deliver('Reviews').deliver('Coupons'); SfChronicle.deliver('The weather is still chilly').deliver('Hi Mom! I\'m writing a book');
一个基本的observer pattern 如下:
function Publisher(){ this.subscribers = []; } Publisher.prototype.deliver = function(data){ this.subscribers.forEach(function(fn){ fn(data); }) }
//Subscriber 的基本构架
funcion Subscriber(data){ console.log(data); } Subscriber.prototype.subscribe = function(publisher) { var that = this; var alreadyExists = publisher.subscribers.some( function(el) { if ( el === that ) { return; } } ); if ( !alreadyExists ) { publisher.subscribers.push(this); } return this; }; Subscriber.prototype.unsubscribe = function(publisher) { var that = this; publisher.subscribers = publisher.subscribers.filter( function(el) { if ( el !== that ) { return el; } } ); return this; };
var publisherObject = new Publisher;
Subscriber.subscribe(publisherObject);
有了observer pattern ,我们不在对浏览器的事件进行绑定(click,load,blur,mouseover......)。我们可以对应用事件进行侦听。比如:drap , moved ,complete,或者 tabSwitch。这些从浏览器中抽象出来的事件就是一个可以被观察的对象,他们可以把消息传递给对他们感兴趣的对象。
案例2 : 动画
这个场景下,最少有三个对象是可以进行观察的,即3个publisher ,start 、finish 和 during 。我们可以对应调用 onStart、onComplete 和 onTween。
// Publisher API var Animation = function(o) { this.onStart = new Publisher, this.onComplete = new Publisher, this.onTween = new Publisher; }; Animation. method('fly', function() { // begin animation this.onStart.deliver(); for ( ... ) { // loop through frames // deliver frame number this.onTween.deliver(i); } // end animation this.onComplete.deliver(); }); // setup an account with the animation manager var Superman = new Animation({...config properties...}); // Begin implementing subscribers var putOnCape = function(i) { }; var takeOffCape = function(i) { }; putOnCape.subscribe(Superman.onStart); takeOffCape.subscribe(Superman.onComplete); // fly can be called anywhere Superman.fly(); // for instance: addEvent(element, 'click', function() { Superman.fly(); });
浏览器事件监听模式 也是 Observers pattern
// example using listeners var element = $('example'); var fn1 = function(e) { // handle click }; var fn2 = function(e) { // do other stuff with click }; addEvent(element, 'click', fn1); addEvent(element, 'click', fn2);
提醒,如果用event handler ,那么后面的处理函数,会覆盖前面的处理函数
// example using handlers var element = document.getElementById('b'); var fn1 = function(e) { // handle click }; var fn2 = function(e) { // do other stuff with click }; element.onclick = fn1; element.onclick = fn2; //fn2 会覆盖fn1