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