php教程:php设计模式介绍之观测模式
p; EmailErrorLogger的类, 它们是观测客体(即观测者)。FileErrorLogger类将出错信息写入日志文件,EmailErrorLogger类利用电子邮件发送出错信息。在UML中,可以表示如下:
为了实现以观测模式为基础的错误记录句柄,首先我们注意到作为观测者的FileErrorLogger类和EmailErrorLogger类什么也不能做。那么,FileErrorLogger类是如何向一个文件写出错信息,EmailErrorLogger类又如何发送电子邮件的? 接下来,让我来看看用来实现观测模式的技术细节,然后,再集中精力来看看该模式的主体――ErrorHandler的细节。最后,再写一些错误处理函数来调用这个ErrorHandler类。 最后用下面的这一段代码来表示:
set_error_handler(‘observer_error_handler’); // ... later
为了使这次观测的操作生效,你的测试必须能证明所有的这些操作(将错误信息写入日志,利用电子邮件发送错误信息)都能得到执行,并且能正常工作。简而言之,让我们来看看一系列简单的测试。(和这个实例有关的其他更多实例,可以在本书附带的源代码中找到) 这里有FileErrorLogger类联合测试的一部分代码:它用来测试当FileErrorlogger类被某个对象实例化时,是否具有向一个文件写日志的能力。
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
var
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
function setup() { function TestRequiresFileHandleToInstantiate() { /* ... */ } function TestWrite() { $log->write($content); function TestWriteIsTimeStamped() { /* ... */ } } 在这个测试中, setup()函数创建了一个文件指针,指向一个名为“test.log”的新文件。并且,将该指针保存在变量
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
(这个测试要求PHP必须具有向那个新建的test.log中写数据的权限。) 下面的一些代码也许可以帮助FileErrorLogger类通过测试。 class FileErrorLogger {
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
function FileErrorLogger($file_handle) { $this->_fh = $file_handle; } function write($msg) { fwrite($this->_fh, date(‘Y-m-d H:i:s: ‘).$msg); } } 一个类似的测试代码可以使EmailErrorLogger类生效。
function TestMail() { 接下来,通过这个测试的EmailErrorLogger类的代码如下:
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
var
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
function EmailErrorLogger($addr, function mail($msg) { 你是怎样确定EmailErrorLogger类能真正发送电子邮件的呢?是的,你可以打开你的收件箱,看看其中是否有新邮件,就知道了。但是,那就不是一个全自动的测试了。 或者说,这个测试就只是伪模式的一个不错的替代方案。(至于如何创建一个控制邮件的类,将作为一个练习留给读者的。详细信息,请参考第6章The MockObject Pattern 或参考FakeMail项目http://sf.net/projects/fakemail/.)
有了合适而正确的观测者,我们就可以在观测模式下,从函数attach()开始继续测试ErrorHandler类。
class ErrorHandlerTestCase extends UnitTestCase { $eh->attach($observer); } 在这次测试中,一个简单的观测类被创建出来,作为所有观测者的接口。为了测试函数attach(),一个基于这个观测类的伪模式被创建出来,并且和ErrorHandler测试实例关联在一起。然后,当公共函数notify()被调用时,伪模式将证实update()函数曾经被调用过。 请注意刚才提及的的在模拟观测中所创建的函数array(&$eh)中的参数。在理想状态中,那个测试应该可以通过的。然而,由于PHP语言的限制,这将产生一个致命错误:“Nesting Level Too Deep――循环依赖?”。为了避免出现那样的问题,代码中必须使用简单测试下“Wild Card”功能,以便允许所有参数都能像预期的那样传递。 ? Nesting Level Too Deep 因为ErrorHandler在数组
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
ErrorHandler开始应该像下面这样构造:
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
function attach(&$observer) { $this->_observers[] =& $observer; } function notify() { 根据上面的代码,你必须在每一个具体的观测者中添加一个update()函数。在每个实例中,update()函数需要知道如何从被观测者ErrorHandler类中获取信息,进而执行自身的相应功能。这里是添加的代码。
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
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 {
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
var
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
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()就更加合适,因为这两个名字更加贴近这个函数的功能。
另外,如果你不喜欢该模式下对象与对象之间的连接方式,你可以更改update()函数让它来发送一个信息(类似于本例中的错误信息数组或者几个信息对象)来避免引用自己。 这里是一个全新的ErrorHandler,不仅做了最新的修改并且还包含detach()函数: class ErrorHandler {
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
var
Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved 地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008 电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134 《中华人民共和国增值电信业务经营许可证》闽B2-20100024 ICP备案:闽ICP备05037997号
function attach(&$observer) { $this->_observers[] =& $observer; } function detach(&$observer) { foreach(array_keys($this->_observers) as $key) { if ($this->_observers[$key] === $observer) { unset($this->_observers[$key]); return; } } } function notify() { foreach(array_keys($this->_observers) as $key) { $observer =& $this->_observers[$key]; $observer->update($this); } } function getState() { return $this->_error_info; } function setState($info) { $this->_error_info = $info; $this->notify(); } } 你现在已经拥有了观测模式下的一个完整工具。 现在,回到本章的原始目标中,让我们看看如何在一个真正的PHP脚本中应用ErrorHandler。为了在一个PHP应用中包含观测者,你必须实例化ErrorHandler类,并确认函数set_error_handler()使用完全相同的参数。这听起来就像最近的一个问题:单件模式。 让我们作一个Factory()函数,它是一个简单的PHP函数,可以返回ErrorHandler的单态实例。 function &getErrorHandlerInstance() { 现在,让我们写一个错误记录句柄功能来获取单态ErrorHandler,改变它的状态来反映错误,并且通知“观测者”。
,’msg’ => $errstr 也许你会注意到这里并没有ErrorHandler::notify()函数。为什么呢?因为ErrorHandler不论何时,只要状态一改变就会自动发出通知。 class ErrorHandler { 这种“默认通知”的方法,有利有弊。但先进之处在于客户端代码不需要包含通知的触发代码。 当然,如果主体对象的状态有好几处变化,所有的变动都对应不同的函数,你就可以选择让客体代码强制调用notify()函数。 自从你能正确使用这些辅助工具后,你给ErrorHandler添加的另一种类型的记录方式就会变得相当的容易?你现在只需要拥有向系统中写日志的权限。稍微查一下PHP手册(http://www.php.net/syslog),你就可以找到一些非常有用的函数来建立日志系统。这些可以很容易的被封装到一个新的类里,以便和ErrorHandler联合使用。
function log($msg) { function update(&$error_handler) { 注:错误日志的用处 结论 观测模式是非常有用的。这里的例子是完全静态的--观测者可以在脚本的初始化阶段被配置且被生成。要想展示观测模式的灵活性,最好是在一个更加动态的应用中--你需要根据脚本中的其他事情来添加或删除观测者。以常见的“生存时间”或者说该PHP脚本的允许执行时间打个比方,当同一个脚本在不同的情况下执行时,就可以根据不同的观测者分别配置,而不需要动态改变一个脚本的流程。这就和通过延长脚本执行时间的PHP-GTK库有很大不同。 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |