首 页   · 站长博客 · 用户注册 · 会员登陆  · 会员排行  ·最新主题  ·最近回复  精华区  版权声明  ·论坛管理
  当前登录身份:游客,请先登录。  笔名: 口令: 验证码:   
楼 主  index »  PHP相关资源下载区 » 《PHP设计模式介绍》第三章 工厂模式(3)  


  作者:lxshark
  注册时间:2009-08-06
  主题/回复:49/50
  积分:639
  等级:★★★(六级)
  称号:声名鹊起

  lxshark@yeah.net..
  593993745
  hi.baidu.com/mythicsky

 

 发表:2009-08-06 15:56:45 阅读 3044 次 回复 1 次 得分0  |   字号 字色
《PHP设计模式介绍》第三章 工厂模式(3)
为了满足前面的测试,PropertyInfo 类定义为:


class PropertyInfo {
const TYPE_KEY = 0;
const PRICE_KEY = 1;
const COLOR_KEY = 2;
const RENT_KEY = 3;
public $type;
public $price;
public $color;
public $rent;
public function __construct($props) {
$this->type =
$this->propValue($props, ‘type’, self::TYPE_KEY);
$this->price =
$this->propValue($props, ‘price’, self::PRICE_KEY);
$this->color =
$this->propValue($props, ‘color’, self::COLOR_KEY);
$this->rent =
$this->propValue($props, ‘rent’, self::RENT_KEY);
}
protected function propValue($props, $prop, $key) {
if (array_key_exists($key, $props)) {
return $this->$prop = $props[$key];
}
}
}
 
现在PropertyInfo 类可以构造各种不同的Property参数了。同时Assessor类可以提供数据来建立正确的PropertyInfo对象。
现在以Assessor->$prop_info数组提供的数据为基础,新建一个实例化 PropertyInfo 的类。
这样的代码可以是:



class Assessor {
protected $game;
public function setGame($game) { $this->game = $game; }
public function getProperty($name) {
$prop_info = new PropertyInfo($this->prop_info[$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(/* ... */);
}
 
这段代码实现了上述功能, 但却非常脆弱。如果代入的值是$this->prop_info数组中没有的值,结果会怎样呢?因为 PropertyInfo 已经被实例化并被加入到Assessor代码中, 没有有效的方法测试被产生的对象。比较好的解决就是:产生一个工厂方法使 PropertyInfo 对象更容易建立。 因此, 下一步将是写一个测试来实现Assessor类中的PropertyInfo方法。


但是,有一个问题: 这个方法不应该是Assessor类的公共接口(API)的一个部份。它能被测试吗?

这里有两个方法, 可以探究任何要求的合理数量的测试。简单的说, 你可以运行黑匣子测试或白匣子测试。

注:黑匣子测试(Black Box Testing)
黑匣子测试就是:把被测试的对象当成" 黑匣子 " ,我们只知道它提供的应用接口(API),但不知道其到底执行了什么。它主要测试对象公共方法的输入和输出。
白匣子测试(White Box Testing)
白匣子测试和黑匣子测试恰恰相反, 它假定知道测试对象中的所有代码信息。这种形式的测试是为了完善代码和减少错误。
关于白匣子测试的详细说明请见:http:// c 2.com/cgi/wiki?WhiteBoxTesting 。

别把话题扯远了。那么如何才在黑匣子和白匣子之间找到折中办法来实现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技术中的多态也是很重要的。



function TestGetRgbRed() {
$red =& new Color(255,0,0);
$this->assertEqual(‘#FF0000’, $red->getRgb());


 
 1#楼  
 
  回复人:lxshark
  注册时间:2009-08-06
  主题/回复:49/50
  积分:639
  等级:★★★(六级)
  称号:声名鹊起

   
 1#楼 发表于2009-08-08 21:37:16  评分:× 

回复给楼主(lxshark)
希望能对你们更好的学习PHP有所帮助,也期望大家有时间多光顾我的
点此打开链接:http://hi.baidu.com/mythicsky
  页数1/1首页 « 1 » 末页
  发表回复:您还没有登陆,无法发表回复。请先[登陆]

一起PHP技术联盟 主办:一起PHP 联系方式:站长QQ:4304410 QQ群:8423742 20159565 站长博客 E-mail: nqp@nqp.me 执行时间:0.013sec
SimsmaBBS 2008 (v6.0) Developed by 17php.com,Copyright(C)2003-2010 All rights reserved. 副本授权:一起PHP官方专用版