WebjxCom提示:在面向对象
编程中,最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的
。但是在一些情况下,new操作符直接生成对象会带来一些问题
。举例来说,许多类型对象的创造需要一系列的步骤:你可能需要计算或取得对象的初始设置;选择生成哪个子对象
别把话题扯远了。那么如何才在黑匣子和白匣子之间找到折中办法来实现TDD呢呢?一种选择就是使原来的类中的私有方法变为公有
,并且在发布的时候变回私有。但这并不是十分令人满意的方式,所以我们建立一个子类
,同时使子类中的方法可以从外部访问:
下面就是一个子类的例子:
classTestableAssessorextendsAssessor{
publicfunctiongetPropInfo($name){
returnAssessor::getPropInfo($name);
}
}
这样做的好处是你可以得到正确的Assessor公有接口(API),但通过TestableAssessor类我们就可以来测试Assessor类了。另外,你用于测试的代码也不会影响到Assessor类。
缺点是:外加的类会带来更多的问题,从而使测试变得更复杂。而且如果你在对象中的一些内部接口作出一些改动,你的测试将随着你的重构而再次失效。
比较了它的优点和缺点,让我们来看看它的测试方法:
functiontestGetPropInfoReturn(){
$assessor=newTestableAssessor;
$this->assertIsA(
$assessor->getPropInfo(‘Boardwalk’),‘PropertyInfo’);
}
为了要保证所有代码的正确执行,我们可以使用异常处理。SimpleTest的目前是基于
PHP4搭建的测试的结构,所以不具备异常处理能力。但是你还是可以在测试中使用如下。
functiontestBadPropNameReturnsException(){
$assessor=newTestableAssessor;
$exception_caught=false;
try{$assessor->getPropInfo(‘MainStreet’);}
catch(InvalidPropertyNameException$e){
$exception_caught=true;
}
$this->assertTrue($exception_caught);
最后,Assessor类的执行部分完成了:
classAssessor{
protected$game;
publicfunctionsetGame($game){$this->game=$game;}
publicfunctiongetProperty($name){
$prop_info=$this->getPropInfo($name);
switch($prop_info->type){
case‘Street’:
$prop=newStreet($this->game,$name,$prop_info->price);
$prop->color=$prop_info->color;
$prop->setRent($prop_info->rent);
return$prop;
case‘RailRoad’:
returnnewRailRoad($this->game,$name,$prop_info->price);
break;
case‘Utility’:
returnnewUtility($this->game,$name,$prop_info->price);
break;
default://shouldnotbeabletogethere
}
}
protected$prop_info=array(/*...*/);
protectedfunctiongetPropInfo($name){
if(!array_key_exists($name,$this->prop_info)){
thrownewInvalidPropertyNameException($name);
}
returnnewPropertyInfo($this->prop_info[$name]);
}
}
Assessor::getPropInfo()方法从逻辑上说明PropertyInfo工厂类是作为了Assessor类的一个私有的方法。而Assessor::getProperty()方法是用来返回三个Property子类的一个,至于返回哪一个子类这要看property的名字。
迟加载(LazyLoading)的工厂
使用工厂的另一个好处就是它具有迟加载的能力。这种情况常被用在:一个工厂中包括很多子类,这些子类被定义在单独的
PHP文件内。
注:术语-迟加载
在迟加载模式中是不预加载所有的操作(像包含PHP文件或者执行数据库查询语句),除非脚本中声明要加载。
用一个脚本可以有效地控制多个网页的输出,这是Web常用的方法了。比如一个博客程序,一些入口就有不同的页面来实现,一个简单的评论入口就有:发布评论的页面,一个导航的页面,一个管理员编辑的页面等。你可以把所有的功能放入一个单独的类中,使用工厂来加载他们。每一个功能类可以单独放在一个文件里,再把这些文件都放在“pages”这个子文件夹里,这样可以方便调用。
实现迟加载的页面工厂(pagefactory)的代码可以写作:
classPageFactory{
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’;
}
returnnew$pageclass;
}
}
你可以利用PHP的动态加载性质,然后使用实时的运行需求(run-time)来给你要建立的类命名。在这情况下,根据一个HTTP请求叁数就能确定哪个页面被加载。你可以使用迟加载,这样只要当你需要建立新对象时才载入相应的类,不需要你载入所有可能用到的“page”类。在上述例子中就用了require_once来实现这一点。这个技术对于一个装有PHP加速器的系统来说并不重要,因为包含一个外加的文件使用的时间对它来说可以忽略。但对于大多数典型的PHP
服务器来说,这样做是很有好处的。
要想了解更多的关于迟加载的知识,请看第11章-代理模式。
小节
工厂模式是非常简单而且非常有用。如果你已经有很多关于工厂模式的例子代码,你会发现更多的东西。《GoF》这本书就介绍了一些关于构建的模式:AbstractFactoryandBuilder。AbstractFactory用来处理一些相关组件,Builder模式则是使建立复杂对象更为容易。
在这章的多数例子里,参数是通过工厂方法引入的(例如CrayonBox::getColor(‘红色’);)。《GoF》中则称为“参数化工厂”(parameterizedfactory),它是PHP
网页设计中典型的工厂方法。
你现在已经了解工厂模式了,它是一种代码中建立新对象的管理技术。你可以看到工厂模式是可以把复杂对象的建立集中起来,甚至用不同的类代替不同的对象。最后,工厂模式支持OOP技术中的多态也是很重要的。