别把话题扯远了。那么如何才在黑匣子和白匣子之间找到折中办法来实现TDD呢呢?一种选择就是使原来的类中的私有方法变为公有,并且在发布的时候变回私有。但这并不是十分令人满意的方式,所以我们建立一个子类,同时使子类中的方法可以从外部访问:
下面就是一个子类的例子:
class TestableAssessor extends Assessor { public function getPropInfo($name) { return Assessor::getPropInfo($name); } }
这样做的好处是你可以得到正确的Assessor公有接口(API), 但通过 TestableAssessor 类我们就可以来测试Assessor类了。另外, 你用于测试的代码也不会影响到Assessor类。
缺点是:外加的类会带来更多的问题,从而使测试变得更复杂。而且如果你在对象中的一些内部接口作出一些改动, 你的测试将随着你的重构而再次失效。
比较了它的优点和缺点,让我们来看看它的测试方法:
function testGetPropInfoReturn() { $assessor = new TestableAssessor; $this->assertIsA( $assessor->getPropInfo(‘Boardwalk’), ‘PropertyInfo’); }
为了要保证所有代码的正确执行, 我们可以使用异常处理。 SimpleTest的目前是基于PHP4 搭建的测试的结构,所以不具备异常处理能力。但是你还是可以在测试中使用如下。
function testBadPropNameReturnsException() { $assessor = new TestableAssessor; $exception_caught = false; try { $assessor->getPropInfo(‘Main Street’); } catch (InvalidPropertyNameException $e) { $exception_caught = true; } $this->assertTrue($exception_caught);
最后, Assessor类的执行部分完成了:
class Assessor { protected $game; public function setGame($game) { $this->game = $game; } public function getProperty($name) { $prop_info = $this->getPropInfo($name); switch($prop_info->type) { case ‘Street’: $prop = new Street($this->game, $name, $prop_info->price); $prop->color = $prop_info->color; $prop->setRent($prop_info->rent); return $prop; case ‘RailRoad’: return new RailRoad($this->game, $name, $prop_info->price); break; case ‘Utility’: return new Utility($this->game, $name, $prop_info->price); break; default: //should not be able to get here } } protected $prop_info = array(/* ... */); protected function getPropInfo($name) { if (!array_key_exists($name, $this->prop_info)) { throw new InvalidPropertyNameException($name); } return new PropertyInfo($this->prop_info[$name]); } }
Assessor::getPropInfo()方法从逻辑上说明 PropertyInfo工厂类是作为了Assessor类的一个私有的方法。而Assessor::getProperty() 方法是用来返回三个Property子类的一个,至于返回哪一个子类这要看property的名字。
迟加载(Lazy Loading)的工厂
使用工厂的另一个好处就是它具有迟加载的能力。这种情况常被用在:一个工厂中包括很多子类,这些子类被定义在单独的PHP文件内。
注:术语 - 迟加载 在迟加载模式中是不预加载所有的操作(像包含PHP文件或者执行数据库查询语句),除非脚本中声明要加载。
用一个脚本可以有效地控制多个网页的输出,这是Web常用的方法了。比如一个博客程序,一些入口就有不同的页面来实现,一个简单的评论入口就有:发布评论的页面,一个导航的页面,一个管理员编辑的页面等。 你可以把所有的功能放入一个单独的类中,使用工厂来加载他们。每一个功能类可以单独放在一个文件里,再把这些文件都放在“pages”这个子文件夹里,这样可以方便调用。
实现迟加载的页面工厂(page factory)的代码可以写作:
class PageFactory { function &getPage() { $page = (array_key_exists(‘page’, $_REQUEST)) ? strtolower($_REQUEST[‘page’]) : ‘’; switch ($page) { case ‘entry’: $pageclass = ‘Detail’; break; case ‘edit’: $pageclass = ‘Edit’; break; case ‘comment’: $pageclass = ‘Comment’; break; default: $pageclass = ‘Index’; } if (!class_exists($pageclass)) { require_once ‘pages/’.$pageclass.’.php’; } return new $pageclass; } }
你可以利用 PHP 的动态加载性质,然后使用实时的运行需求(run-time)来给你要建立的类命名。在这情况下, 根据一个 HTTP 请求叁数就能确定哪个页面被加载。你可以使用迟加载,这样只要当你需要建立新对象时才载入相应的类,不需要你载入所有可能用到的“page”类。在上述例子中就用了 require_once来实现这一点。这个技术对于一个装有PHP加速器的系统来说并不重要,因为包含一个外加的文件使用的时间对它来说可以忽略。 但对于大多数典型的PHP服务器来说,这样做是很有好处的。
要想了解更多的关于迟加载的知识,请看第 11 章-代理模式。
小节
工厂模式是非常简单而且非常有用。如果你已经有很多关于工厂模式的例子代码,你会发现更多的东西。《GoF》这本书就介绍了一些关于构建的模式:AbstractFactory and Builder。 AbstractFactory用来处理一些相关组件,Builder模式则是使建立复杂对象更为容易。
在这章的多数例子里, 参数是通过工厂方法引入的(例如 CrayonBox::getColor(‘红色’);)。《GoF》中则称为“参数化工厂”(parameterized factory),它是PHP网页设计中典型的工厂方法。
你现在已经了解工厂模式了, 它是一种代码中建立新对象的管理技术。 你可以看到工厂模式是可以把复杂对象的建立集中起来,甚至用不同的类代替不同的对象。最后,工厂模式支持OOP技术中的多态也是很重要的。
下文:《PHP设计模式介绍》第四章 单条模式
本文链接:http://www.blueidea.com/tech/program/2008/5860.asp
出处:
责任编辑:bluehearts
上一页 php设计模式介绍之工厂模式 [7] 下一页
◎进入论坛网络编程版块参加讨论
|