观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。—-维基百科
概念的东西本身就不好理解,我试图通过现实中的例子来说清楚这个观察者模式。我们知道,世界上最伟大的爱,莫过于母爱,母亲关心你的一切,你吃饱了吗,穿暖了吗,找到女朋友了吗…如果把天下的母亲归为一类,儿女归为一类,我们知道母亲是时刻关注着儿女的变化的,从观察者模式的角度来讲,母亲是观察者,观察着儿女们的一举一动,一切需求,儿女就是被观察者,他们的一切需求母亲都可以获知。那么,我们来做这样一个例子:
<?php //母亲类接口,包括所有关心你的人 interface M{ //需要一个接口来获取儿女的信息 function update($msg); } //儿女类接口,包括所有被关心的人 interface C{ //需要添加观察者的方法 function addObserver(M $obj); //需要删除观察者的方法 function delObserver(M $obj); //需要通知观察者的方法 function noteMsg($msg); } class Mother implements M{ //这个属性用来存放所有子女对TA说的话 private $childtoldtome=array(); //这个方法用来监听子女对TA说的话 public function update($msg){ $this->childtoldtome[]=$msg; } //这个方法用来告诉大家TA子女都跟她说了什么 public function telleveryone(){ echo "我儿子刚才跟我说:\"",$this->childtoldtome[count($this->childtoldtome)-1],"!\"<br/>"; } } class Child implements C{ public $name; public $age; public $sex; private $hasfriend; private $observer=array(); public function __construct($name,$age,$sex,$hasfriend=0){ $this->name=$name; $this->age=$age; $this->sex=$sex; $this->hasfriend=$hasfriend; } //添加一个增加观察者的方法 public function addObserver(M $ob){ if(!in_array($ob,$this->observer)){ $this->observer[]=$ob; } } //添加一个删除观察者的方法 public function delObserver(M $ob){ if(in_array($ob,$this->observer)){ $key=array_search($ob,$this->observer); unset($this->observer[$key]); } } //添加一个通知观察者信息的方法 public function noteMsg($msg){ foreach($this->observer as $allobj){ $allobj->update($msg); } } //定义一个找女朋友的方法 public function getgrilfriend($gname){ $this->hasfriend=1; $this->noteMsg("妈,我有女朋友了!她叫".$gname); } //定义一个涨工资的方法 public function upwage($pay){ $this->noteMsg("妈,我涨工资了!涨了".$pay); } } //实例化一个母亲对象 小明妈 $xiaomingM=new Mother(); //假设小明妈有两个儿子,小明和小李 $xiaoming=new Child("xiaoming",20,"Male"); $xiaoli=new Child("xiaoli",23,"Male"); //给小明和小李分别添加监听者 $xiaoming->addObserver($xiaomingM); $xiaoli->addObserver($xiaomingM); //小明找到女朋友了,告诉他妈 $xiaoming->getgrilfriend("小芳"); $xiaomingM->telleveryone(); //小李涨工资了,告诉他妈 $xiaoli->upwage(1000); $xiaomingM->telleveryone(); ?>
在上面的例子当中,共有两个类,分别继承于两个接口。母亲类有两个方法,第一个方法update用来接受儿子的状态,第二个方法telleveryone用来告诉其他人儿子刚才对他说了什么。儿子类有五个方法,分别是添加观察者,删除观察者,通知观察者,找女朋友和涨工资。当涨工资和找女朋友的方法被调用时,通知观察者的方法就自动被调用了。上面的这个例子中,面向对象的封装,继承和多态我们都用到了。那么,在JS中,这个单例模式怎么样去做呢?
<script> function Mother(){ var childtoldtome=new Array(); this.update=function(msg){ childtoldtome.push(msg); } this.telleveryone=function(){ document.write("我儿子刚才跟我说:\""+childtoldtome[childtoldtome.length-1]+"!\"<br/>"); } } function Child(name,age,sex){ this.name=name; this.age=age; this.sex=sex; var hasfriend=0; var observer=new Array(); //增加观察者的方法,由于javascript不支持多态,我们必须判断传入的参数是否属于Mother的实例 this.addObserver=function(ob){ if(ob instanceof Mother){ observer.push(ob); }else{ throw "Type error,arguments must be a instance of Mother"; } } //删除观察者的方法,也许要对传入参数作出判断 this.delObserver=function(ob){ if(ob instanceof Mother){ //为了删除数组的特定value的值,先反转数组,获取key //然后再反转数组,通过获取到的这个key去删除 observer.reverse(); var key=observer[ob]; observer.reverse(); observer.splice(key,1); }else{ throw "Type error,arguments must be a instance of Mother"; } } //通知观察者信息的方法 this.noteMsg=function(msg){ for (var i=0;i<observer.length;i++){ observer[i].update(msg); } } //定义一个找女朋友的方法 this.getgrilfriend=function(gname){ hasfriend=1; this.noteMsg("妈,我有女朋友了!她叫"+gname); } //定义一个涨工资的方法 this.upwage=function(pay){ this.noteMsg("妈,我涨工资了!涨了"+pay); } } //实例化母亲这个对象 var xiaomingM=new Mother(); //小明妈有两个儿子 var xiaoming=new Child("xiaoming",20,"Male"); var xiaoli=new Child("xiaoli",23,"Male"); //给小明和小李分别添加事件监听者 xiaoming.addObserver(xiaomingM); xiaoli.addObserver(xiaomingM); //小明找到女朋友,告诉他妈 xiaoming.getgrilfriend("小芳"); xiaomingM.telleveryone(); //小李涨工资了,告诉他妈 xiaoli.upwage(1000); xiaomingM.telleveryone(); </script>
在上面的例子当中,共有两个类,分别继承于两个接口。母亲类有两个方法,第一个方法update用来接受儿子的状态,第二个方法telleveryone用来告诉其他人儿子刚才对他说了什么。儿子类有五个方法,分别是添加观察者,删除观察者,通知观察者,找女朋友和涨工资。当涨工资和找女朋友的方法被调用时,通知观察者的方法就自动被调用了。上面的这个例子中,面向对象的封装,继承和多态我们都用到了。那么,在JS中,这个单例模式怎么样去做呢?
从上面的例子可以看出,虽然javascript一样可以实现和php观察者模式的效果,但是还是有一些区别。第一,javascript和php的继承不太一样,所有没有必要去写像php的m和c的接口,第二是javascript不支持多态,所以必须在函数内容对实例化的对象作出判断。
上面的两个例子在特定的情况下都可以作出改进,可以看到每个对象当中的有一些共有的方法都是一样的,如果实例化的对象太多的话,占用的内存是比较大的。PHP可以把这些公用的方法做成静态方法,js可以把这些方法赋给对象的原型,这样相同的信息只在内存中出现一次,会节省一些系统资源。
评论列表: