WebjxCom提示:php教程:php设计模式介绍之观测模式.
有了合适而正确的观测者
,我们就可以在观测模式下
,从函数attach()开始继续测试ErrorHandler类
。 classObserver{
functionupdate(){
die(‘abstractmethod’);
}
}
Mock::Generate(‘Observer’);
classErrorHandlerTestCaseextendsUnitTestCase{
functionTestAttach(){
$eh=&newErrorHandler;
$observer=&newMockObserver($this);
$observer->expectOnce(
‘update’
,array(‘*’));//array(&$eh)
$eh->attach($observer);
$eh->notify();
$observer->tally();
}
functionTestDetach(){/*...*/}
}
在这次测试中,一个简单的观测类被创建出来,作为所有观测者的接口
。为了测试函数attach(),一个基于这个观测类的伪模式被创建出来,并且和ErrorHandler测试实例关联在一起。然后,当公共函数notify()被调用时,伪模式将证实update()函数曾经被调用过。
请注意刚才提及的的在模拟观测中所创建的函数array(&$eh)中的参数。在理想状态中,那个测试应该可以通过的。然而,由于
PHP语言的限制,这将产生一个致命错误:“NestingLevelTooDeep――循环依赖?”。为了避免出现那样的问题,代码中必须使用简单测试下“WildCard”功能,以便允许所有参数都能像预期的那样传递。
?NestingLevelTooDeep
因为ErrorHandler在数组$_observer中包含涉及到模拟观测的参数,本来预期是要将它传递给模拟观测的。所以,
PHP产生一个“NestingLevelTooDeep”错误。而循环依赖就像一个初级的PHP问题,甚至可以在一个简单的PHP环境中发现它。(请参考
http://bugs.php.net/bug.php?id=31449.)
ErrorHandler开始应该像下面这样构造:
classErrorHandler{
var$_observers=array();
functionattach(&$observer){
$this->_observers[]=&$observer;
}
functionnotify(){
foreach(array_keys($this->_observers)as$key){
$observer=&$this->_observers[$key];
$observer->update($this);
}
}
根据上面的代码,你必须在每一个具体的观测者中添加一个update()函数。在每个实例中,update()函数需要知道如何从被观测者ErrorHandler类中获取信息,进而执行自身的相应功能。这里是添加的代码。
classFileErrorLogger{
var$_fh;
functionFileErrorLogger($file_handle){
$this->_fh=$file_handle;
}
functionwrite($msg){
fwrite($this->_fh,date(‘Y-m-dH:i:s:‘).$msg);
}
functionupdate(&$error_handler){
$error=$error_handler->getState();
$this->write($error[‘msg’]);
}
}
classEmailErrorLogger{
var$_addr;
var$_subject;
functionEmailErrorLogger($addr,
$subject=’ApplicationErrorMessage’){
$this->_addr=$addr;
$this->_subject=$subject;
}
functionmail($msg){
mail($this->_addr
,$this->_subject
,date(‘Y-m-dH:i:s:‘).$msg);
}
functionupdate(&$error_handler){
$error=$error_handler->getState();
$this->mail($error[‘msg’]);
}
}
上面两个update()函数中的每一个,都需要将ErrorHandler作为参数,以便从中获得错误信息,然后调用一个内部函数,来处理这个错误。每个update()函数通过ErrorHandler中的getState()函数来获取信息。那个函数以getState()命名是为了在GoF模式中,保持模式的整体和谐性。但是,如果将这个函数命名为getError()或者getErrorInfo()就更加合适,因为这两个名字更加贴近这个函数的功能。