观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。—-维基百科
概念的东西本身就不好理解,我试图通过现实中的例子来说清楚这个观察者模式。我们知道,世界上最伟大的爱,莫过于母爱,母亲关心你的一切,你吃饱了吗,穿暖了吗,找到女朋友了吗…如果把天下的母亲归为一类,儿女归为一类,我们知道母亲是时刻关注着儿女的变化的,从观察者模式的角度来讲,母亲是观察者,观察着儿女们的一举一动,一切需求,儿女就是被观察者,他们的一切需求母亲都可以获知。那么,我们来做这样一个例子:
<?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可以把这些方法赋给对象的原型,这样相同的信息只在内存中出现一次,会节省一些系统资源。
评论列表: