案例
黑枣快运公司的快递系统把公司每一笔新的快递单通知到快递员的手持终端上
<实现代码>
class expressData
{
public $info;
/**
* 新快递单信息(此方法为简单实现,实际可能为按照快递单号从数据库查询)
* @return array
*/
public function newInfo()
{
$this->info = array(
'id' => '833737773',
'from' => '青岛',
'to' => '北京'
);
return $this->info;
}
public function getInfo()
{
return $this->info;
}
public function changed()
{
$this->notify();
}
/**
* 通知
* @return array
*/
public function notify()
{
$info = $this->getInfo();
echo '快递单号:' . $info['id'] . "\n";
echo ' 始发地:' . $info['from'] . "\n";
echo ' 目的地:' . $info['to'] . "\n";
}
}
class testDriver
{
public function run()
{
$expressinfo = new expressData();
//新的快递单
$expressinfo->newInfo();
//再将快递单信息输出到电脑
$expressinfo->changed();
}
}
$test = new testDriver();
$test->run();
结果:
<系统要升级>
黑枣快运公司的快递系统把公司每一笔新的快递单通知到快递员的手持终端上,还有快递员他老婆的手机上,告诉快递员他老婆别因为快递员老不回家犯嘀咕。
<实现代码重构>
class expressData
{
public $info;
/**
* 新快递单信息(此方法为简单实现,实际可能为按照快递单号从数据库查询)
* @return array
*/
public function newInfo()
{
$this->info = array(
'id' => '833737773',
'from' => '青岛',
'to' => '北京'
);
return $this->info;
}
public function getInfo()
{
return $this->info;
}
public function changed()
{
$this->notifyToHim();
$this->notifyToHisWife();
}
/**
* 通知
* @return array
*/
public function notifyToHim()
{
$info = $this->newInfo();
echo "Notify-->Him\n";
echo '快递单号:' . $info['id'] . "\n";
echo ' 始发地:' . $info['from'] . "\n";
echo ' 目的地:' . $info['to'] . "\n";
}
public function notifyToHisWife()
{
$info = $this->getInfo();
echo "Notify-->HisWife\n";
echo '快递单号:【' . $info['id'] . "】\n";
echo ' 始发地:【' . $info['from'] . "】 \n";
echo ' 目的地:【' . $info['to'] . "】 \n";
}
}
class testDriver
{
public function run()
{
$expressinfo = new expressData();
//新的快递单
$expressinfo->newInfo();
//再将快递单信息输出到电脑
$expressinfo->changed();
}
}
$test = new testDriver();
$test->run();
输出:
<系统还要升级>
黑枣快运公司的快递系统把公司每一笔新的快递单通知到快递员手持终端、老婆的手机、丈母娘手机、儿子手机、老爸手机、老妈手机、女儿手机....
<实现代码重构>
程序猿已经崩溃了
分析OOA:
expressData类做的事情太多了。当中心对象与周边多个对象发生紧密关联时,如何把中心对象从中解脱出来,实现中心对象与周边对象的低耦合。在本案例中,快递员手持终端、老婆的手机可以抽象为观察者,快递单信息为被观察者。使用观察者模式。
设计OOD:
<UML>
<说明>
1.Subject为被观察者接口,定义addObserver添加观察者、delObserver删除观察者、通知观察者三个方法。
2.ExpressData类为被观察者类,实现了Subject接口三个方法,并包含一个observers数组,用于记录观察者记录。ExpressData类有一个changed的方法,该方法调用notifyObservers来通知观察者。
3.Observer为观察者更新接口,定义update方法,接收被观察者ExpressData的通知数据。
4.Display为观察者显示接口,定义display方法,将被观察者ExpressData的通知数据实时显示。
5.HisObserver及WifeObserver都是观察者,实现了Observer及Display接口。在本例中分别为快递员手持终端跟快递员老婆的手机。
编程 OOP:
<代码>
interface Observer{
public function update($expressinfo);
}
interface Display{
public function display();
}
class HisObserver implements Observer,Display{
private $info;
public function update($expressinfo){
$this->info = $expressinfo;
$this->display();
}
public function display(){
echo "Notify-->Him\n";
echo '快递单号:' . $this->info['id'] . "\n";
echo ' 始发地:' . $this->info['from'] . "\n";
echo ' 目的地:' . $this->info['to'] . "\n";
}
}
class WifeObserver implements Observer, Display
{
private $info;
public function update($expressinfo)
{
$this->info = $expressinfo;
$this->display();
}
public function display()
{
echo "Notify-->HisWife\n";
echo '快递单号:【' . $this->info['id'] . "】\n";
echo ' 始发地:【' . $this->info['from'] . "】 \n";
echo ' 目的地:【' . $this->info['to'] . "】 \n";
}
}
interface Subject
{
/**
* @abstract 新加观察者
* @param $observer
* @return mixed
*/
public function addObserver($observer);
/**
* @abstract 删除观察者
* @param $observer
* @return mixed
*/
public function delObserver($observer);
/**
* @abstract 通知观察者
* @return mixed
*/
public function notifyObservers();
}
class ExpressData implements subject{
private $info;
private $observers = array();
public function addObserver($observer){
$hash = spl_object_hash($observer);
$this->observers[$hash] = $observer;
}
public function delObserver($observer)
{
$hash = spl_object_hash($observer);
if(isset($this->observers[$hash])) unset($this->observers[$hash]);
}
public function notifyObservers()
{
$info = $this->getInfo();
foreach($this->observers as $oneserver){
$oneserver->update($info);
}
}
/**
* 新快递单信息(此方法为简单实现,实际可能为按照快递单号从数据库查询)
* @return array
*/
public function newInfo()
{
$this->info = array(
'id' => '833737773',
'from' => '青岛',
'to' => '北京'
);
return $this->info;
}
public function getInfo()
{
return $this->info;
}
public function changed()
{
$this->notifyObservers();
}
}
测试用例Test Case:
<代码>
class testDriver
{
public function run()
{
$expressinfo = new expressData();
echo "添加观察者:快递员\n";
$observer_his = new HisObserver();
$expressinfo->addObserver($observer_his);
echo "添加观察者:快递员老婆\n";
$observer_wife = new WifeObserver();
$expressinfo->addObserver($observer_wife);
echo "新的快递单来了,通知所有观察者\n";
$expressinfo->newInfo();
$expressinfo->changed();
echo "-------------------\n";
echo "删除观察者:快递员老婆\n";
$expressinfo->delObserver($observer_wife);
echo "新的快递单又来了,通知所有观察者\n";
$expressinfo->newInfo();
$expressinfo->changed();
}
}
$test = new testDriver();
$test->run();
<输出>
小结:
在本例中,被观察者subject使用update方法主动把信息通知(推给)各观察者,观察者也可以使用拉的方式向被观察者(拉取)信息,不过要被观察者提供一些公开的getter类方法。目前的微薄关注、主从服务器同步都应用了观察者模式。
观察者模式把中心对象与周边对象解耦了。围观者不用知道中心对象的内部实现,只知道实现观察者接口就行了
PHP的SPL库也实现了观察者模式接口SplObserver及SplSubject
********************************************
* 作者:叶文涛
* 标题:Php设计模式之观察者模式(一)
* 时间:2012-5-13
* 参考:
*《PHP设计模式》(美)Aaron Saray著
*《PHP高级程序设计模式、框架与测试》(加)Kevin McArthur著
*《Head First设计模式》Eric Freeman等著
******************转载请注明来源 ***************