js 设计模式 第十五章 Observer Pattern

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);

对于observable ,应该有三个函数,subsribeCustomer ,unSubscribeCustomer ,和 deliver

这个模型中,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');

这个场景中,subscriber 拥有subscribe 和 unsubscribe 的权利,当然,publisher 掌握了deliver 的权利



一个基本的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();
});

案例3  浏览器事件监听模式

浏览器事件监听模式 也是 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);

一个dom 元素,用侦听模式,可以为click 增加无数个处理事件,每个处理事件都是独立的。

提醒,如果用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





你可能感兴趣的:(js 设计模式 第十五章 Observer Pattern)