一、定义
在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式(又名发布者-订阅者(publisher-subscripber)模式)是一种管理人与其任务之间的关系(确切地讲,是对象及其行为和状态之间的关系)的得力工具.用JavaScript的话来说,这种模式的实质就是你可以对程序中某个对象的状态进行观察,并且在其发生改变时能够得到通知。
二、例子
我们需要一个发布者的构造函数,它为该实例定义了一个类型为数组的属性,用来保存订阅者的引用。
function Publisher() {
this.subscribers = [];
}
接下来,构建订阅方法。所有Publisher实例都应该能够投送数据。只要把deliver方法添加到Publisher的prototype中,它就能够被所有Publisher对象共享。
// 订阅者订阅能力
Function.prototype.subscribe = function(publisher) {
var that = this,
alreadyExists = false,
i,
len = publisher.length; for(i = 0; i < len; i++) {
if (el === this) {
alreadyExists = true;
}
} if (!alreadyExists) {
publisher.subscribers.push(this);
} return this;
}; // 订阅者具有退订能力
Function.prototype.unSubscribe = function(publisher) {
var that = this,
array = [],
len = publisher.length; for(i = 0; i < len; i++) {
if (el !== this) {
array.push(this);
}
} publisher.subscribers = array; return this;
};
应用之——
// 订阅者订阅能力
Function.prototype.subscribe = function(publisher) {
var that = this,
alreadyExists = false,
i,
len = publisher.length; for(i = 0; i < len; i++) {
if (el === this) {
alreadyExists = true;
}
} if (!alreadyExists) {
publisher.subscribers.push(this);
} return this;
}; // 订阅者具有退订能力
Function.prototype.unSubscribe = function(publisher) {
var that = this,
array = [],
len = publisher.length; for(i = 0; i < len; i++) {
if (el !== this) {
array.push(this);
}
} publisher.subscribers = array; return this;
};
三、现实世界中的观察者
在现实世界中,观察者模式对于那种由许多JavaScript程序员合作开发的大型应用程序特别有用。它可以提高API的灵活性,使并行开发的多个市县能够彼此独立的进行修改。作为开发人员,你可以对自己的应用程序中什么是“令人感兴趣的时刻”做出决定。你所能监听的不再只是click、load、blur和mouseover等浏览器事件。在富用户界面应用程序中,drag(拖动)、drop(拖放)、moved(移动)、complete(完成)和tabSwitch(标签切换)都可能是令人感兴趣的事件。它们都是在普通浏览器事件的基础上抽象出来的可观察事件,可由发布者对象向其监听者广播。
在DOM脚本编程环境中的高级时间模式中,事件监听器说到底就是一种内置的观察者。事件处理器(handler)与事件监听器(listener)并不是一回事。前者说穿了就是一种把事件传递给与其关联的函数的手段。而且在这种模型中一种事件只能指定一个回调方法。而在监听器模式中,一个事件可以与几个监听器关联。每个监听器都能独立于其他监听器而改变。
// 使用事件监听器,可以让多个函数响应同一个事件
var element = $('#example');
var fn1 = function(e) {
// handle clikc
};
var fn2 = function(e) {
// do other stuff with click
};
// 由于使用的是事件监听器,所以click事件发生时fn1和fn2都会被调用
addEvent(element, 'click', fn1);
addEvent(element, 'click', fn2);
// 但用事件处理器就办不到
var element = $('#example');
var fn1 = function(e) {
// handle clikc
};
var fn2 = function(e) {
// do other stuff with click
};
element.onclick = fn1;
// 第二个onclick赋值的结果是fn1被fn2取代,因此click事件发生时只会调用fn2。
element.onclick = fn2;
监听器和观察者之间的共同之处显而易见。实际上它们互为同义词。它们都订阅特定的事件,然后等待事件的发生。事件发生时,订阅方的回调函数会得到通知。传给它们的参数是一个事件对象,其中包含着事件发生时间、事件类型和事件发源地等有用的信息。
四、优势
观察者模式可以削减为事件注册监听器的次数,让可观察对象借助一个事件监听器替你处理各种行为并将信息委托给它的所有订阅者,从而降低内存消耗和提高互动性能,提高程序的可维护性。
五、劣势
使用这种观察者接口的一个不利之处在于创建可观察对象所带来的加载时间开销。这可以通过惰性加载技术加以化解。
六、小结
一个事件可以被5个订阅者订阅,而一个订阅者也可以订阅5个不同的事件。对于浏览器这类互动环境来说这非常理想。现在的Web应用程序越来越大,在此背景下,作为一种提高代码的可维护性和简洁性的有力手段,可观察对象的作用更显突出。