设计模式(Design Pattern)对于软件开发来讲其重要性不言而喻,代码可复用、可维护、可扩展一直都是软件工程中的追求!对于我一个学javascript的人来讲,理解设计模式彷佛有些困难,对仅切图、作少许交互效果的FE甚至可能不会用到,可是当你开始使用Angular/Backbone等框架的时候,就没法避免设计模式、MVC/MVVM这些东西了(反正我是伤脑筋)。javascript
我学设计模式是刚开始接触编程大概三个月的时候,看一本书《大话设计模式》,里面用C#语言来写,我很无语,由于强类型的编程语言对于我这种写弱类型的毛头小子来讲,彷佛又有困难啊,因而我就学C#基础语法规则去了。。。今年年初我又学了JAVA的基础语法规则。。。然而个人初衷已经被抛弃在一旁,落上了厚厚的灰层。对于自学编程的我来讲,不知道学习编程的前后顺序彷佛吃亏很多,可是总要有开头的!html
以上可直接跳过java
先来讲一下我对“观察者模式”的我的理解:观察者模式又称“发布-订阅(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)。在实际项目中,发布者的内容多是从后台获取的也多是从前台某地方输入的。然而发布者每次更新内容后又要手动调用通知是否是有点多余呢?既然更新了内容那就确定要通知别人了啊。那咱们就把内容的更新与发出通知进行绑定好了,看下面的代码:函数
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script type="text/javascript"> //发布者 function Publisher(){ this.observers = []; var state = ""; //让该内容不能直接访问 //新增两个对于state的操做 获取/更新 this.getState=function(){ return state; } this.setState=function(value){ state = value; this.notice(); } } 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.getState()); //获取发布者的内容 }; } //订阅者 function Subscribe(){ this.update = function(data){ console.log(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.setState("open "); //发布者更新了内容 </script> </body> </html>
对于以上的内容,或许并无跟咱们的项目中实际出现的问题有关,那咱们就来代入这种设计模式,作一个例子:三个文本框ABC,其中A可编辑,B与C不可编辑且B的值是A的值加上后缀"@w3c.com",C的值是A的值加上前缀"ID-"。学习
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div> <label>用户名称:<input type="text" id="pba" placeholder="请输入用户名称" /></label><br /><br /> <label>生成邮箱:<input type="text" id="oba" readonly /></label> <label>生成ID:<input type="text" id="obb" readonly /></label> </div> <script type="text/javascript"> //发布者 function Publisher(obj){ this.observers = []; var state = obj.value; //让该内容不能直接访问 //新增两个对于state的操做 获取/更新 this.getState=function(){ return state; } this.setState=function(value){ state = value; this.notice(); } this.obj = obj; } 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.getState()); }; } //订阅者 function Subscribe(obj){ this.obj = obj; this.update = function(data){ this.obj.value = data; }; } //实际应用 var oba = new Subscribe(document.querySelector("#oba")), obb = new Subscribe(document.querySelector("#obb")); var pba = new Publisher(document.querySelector("#pba")); pba.addOb(oba); pba.addOb(obb); oba.update = function(state){ this.obj.value = state+"@w3c.com"; } obb.update = function(state){ this.obj.value = "ID-"+state; } pba.obj.addEventListener('keyup',function(){ pba.setState(this.value); }); </script> </body> </html>
在《大话设计模式》一书中,提到相似的状况:若是针对发布者内容而订阅者要作不一样的事情呢?好比一个按钮和三个矩形,点击按钮的时候,第一个矩形增长宽度,第二个矩形增长高度,第三个矩形则变成圆角矩形又该怎么作呢?固然咱们能够在三个矩形的update内部写具体的实现代码,可是这update岂不是没有一个具体的功能描述了吗?该书中用“事件委托”解决了这个问题(此处事件委托和DOM中的事件委托应该是两码事),我我的理解这个“事件委托”在javascript中能够用一个数组表示,而后里面放各个订阅者的不一样名字的update,而后一一调用。
在《javascript设计模式》一书中,关于观察者模式的实现也是采用”推“这种方式,章节的最后反问到如何实现”拉“这种方式呢?
我我的理解:发布者推送数据的时候有强制性,促使订阅者更新(update),然而在”拉“这种模式中,发布者自己仅仅包含最新的内容,没有通知(notice)没有订阅者列表,当订阅者须要获得数据的时候在其对应的update方法里面传入发布者对象便可。小白之见,请对该模式有不一样理解的道友多多指正。o(∩_∩)o