PHP和Javascript的观察者模式对比

观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。—-维基百科

概念的东西本身就不好理解,我试图通过现实中的例子来说清楚这个观察者模式。我们知道,世界上最伟大的爱,莫过于母爱,母亲关心你的一切,你吃饱了吗,穿暖了吗,找到女朋友了吗…如果把天下的母亲归为一类,儿女归为一类,我们知道母亲是时刻关注着儿女的变化的,从观察者模式的角度来讲,母亲是观察者,观察着儿女们的一举一动,一切需求,儿女就是被观察者,他们的一切需求母亲都可以获知。那么,我们来做这样一个例子:

<?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可以把这些方法赋给对象的原型,这样相同的信息只在内存中出现一次,会节省一些系统资源。

发表回复

评论列表:

青春
青春
比喻贴切、易懂,讲解详细,收益匪浅啊!
回复此留言