先来说一下我对“观察者模式”的个人理解:观察者模式又称“发布-订阅(Publish/Subscribe)模式”,发布与订阅显然是两个不同对象的功能,比如RSS。知乎是一个发布者(发布一些对某方面问题的高赞同解答),我作为一个订阅者(在我的邮箱里面订阅了知乎的相关发布内容),我的同事以及我的老板都订阅了知乎,所以在这个模型中,有一个发布者,有三个订阅者。
在具体编程中,发布者有了新的内容,需要向订阅者推送数据,那么新的内容(state)、订阅者有哪些(observers)就是发布者需要包含的东西,谁订阅了、谁退订了则要对发布者中的订阅者列表进行更新。以下是发布者的相关信息代码解读
//发布者 function Publisher(){ this.observers = []; this.state = ""; } Publisher.prototype.addOb=function(observer){ var flag = false; for (var i = this.observers.length - 1; i >= 0; i--) { if(this.observers[i]===observer){ flag=true; } }; if(!flag){ this.observers.push(observer); } return this; } Publisher.prototype.removeOb=function(observer){ var observers = this.observers; for (var i = 0; i < observers.length; i++) { if(observers[i]===observer){ observers.splice(i,1); } }; return this; } Publisher.prototype.notice=function(){ var observers = this.observers; for (var i = 0; i < observers.length; i++) { observers[i].update(this.state); }; }
以上在遍历observers数组的时候,可以使用数组类的filter、forEach等新特性来处理。第三个notice函数表示发布者有了新东西,然后对订阅者列表中的所有人通知他们我有新内容(state)了,你们拿去更新你们的邮箱吧。这里把内容传递给了每一个订阅者的update更新功能。
那么订阅者呢?订阅者很简单,只需要具有一个update功能即可(每一个订阅者update可能不一样,比如我是放进邮箱了,我的同事则将订阅的拿来,并且顺便把旧的删掉了,我的上司则将数据转发到Gmail去了)。下面是订阅者相关信息代码解读:
//订阅者 function Subscribe(){ this.update = function(data){ console.log(data); }; }
实际上,因为每一个订阅者都有这个update,所以我们通常应该将其添加到构造器的原型上面,当对这个默认的update功能不满足要求的时候,可以为每一个订阅者的实例设置单独的update,比如将这个data发送给别人。最后咱们看看怎么应用。
//实际应用 var oba = new Subscribe(), obb = new Subscribe(); var pba = new Publisher(); pba.addOb(oba); pba.addOb(obb); oba.update = function(state){ console.log(state+"hello!"); } obb.update = function(state){ console.log(state+"world!"); } pba.state = "open "; pba.notice();
大家看到,我们在最后对发布者手动设置了它的内容(state)并且要求他发出通知(notice)。在实际项目中,发布者的内容可能是从后台获取的也可能是从前台某地方输入的。然而发布者每次更新内容后又要手动调用通知是不是有点多余呢?既然更新了内容那就肯定要通知别人了啊。那我们就把内容的更新与发出通知进行绑定好了,看下面的代码:
Document
对于以上的内容,或许并没有跟我们的项目中实际出现的问题有关,那我们就来代入这种设计模式,做一个例子:三个文本框ABC,其中A可编辑,B与C不可编辑且B的值是A的值加上后缀"@w3c.com",C的值是A的值加上前缀"ID-"。
Document
在《大话设计模式》一书中,提到类似的情况:如果针对发布者内容而订阅者要做不同的事情呢?比如一个按钮和三个矩形,点击按钮的时候,第一个矩形增加宽度,第二个矩形增加高度,第三个矩形则变成圆角矩形又该怎么做呢?当然我们可以在三个矩形的update内部写具体的实现代码,但是这update岂不是没有一个具体的功能描述了吗?该书中用“事件委托”解决了这个问题(此处事件委托和DOM中的事件委托应该是两码事),我个人理解这个“事件委托”在javascript中可以用一个数组表示,然后里面放各个订阅者的不同名字的update,然后一一调用。