《PHP设计模式介绍》第三章 工厂模式(6)_PHP教程
推荐:《PHP设计模式介绍》第二章 值对象模式在所有的最简单的程序中,大多数对象都有一个标识,一个重要的商业应用对象,例如一个Customer或者一个SKU,有一个或者更多的属性---id,name,email地址,这样可以把它从同一个类的其他实例区分开
“工厂”促进多态
控制被送回对象的内在状态固然重要, 但是如果促进多态即返回相同的接口多种类的对象,可以使得工厂模式的功能更为强大。
让我们再次看一下Monopoly的例子,然后执行购买游戏中的道具的行为。在游戏中,你的任务就是买道具,包括一些基本动作。更进一步说, 有三种不同的道具: Street,RailRoad和Utility。所有三个类型的道具有一些共同点: 每个道具都被一个玩家拥有; 每个都有价格;而且每个都能为它的拥有者产生租金只要其他的玩家在它上面登陆。但道具之间还是存在差异的,举例来说, 计算租金的多少就取决于道具的类型。
下列的代码展示了一个Property的基本类:
// PHP5 |
注:术语 – 基类
一个基类就是不能被直接实例化的类。 一个基础的类包含一个或更多的基础方法,这些方法必须在子类被覆盖。一旦所有的抽象方法被覆盖了, 子类也就产生了。
基类为许多相似的类创造了好的原型。
CalcRent() 方法必须在子类被覆盖,从而形成一个具体的类。因此, 每个子类包括:Street,RailRoad和Utility,和必须定义的calcRent() 方法。
为实现以上的情况,这三个类可以定义为:
class Street extends Property {
protected $base_rent;
public $color;
public function setRent($rent) {
$this->base_rent = new Dollar($rent);
}
protected function calcRent() {
if ($this->game->hasMonopoly($this->owner, $this->color)) {
return $this->base_rent->add($this->base_rent);
}
return $this->base_rent;
}
}
class RailRoad extends Property {
protected function calcRent() {
switch($this->game->railRoadCount($this->owner)) {
case 1: return new Dollar(25);
case 2: return new Dollar(50);
case 3: return new Dollar(100);
case 4: return new Dollar(200);
default: return new Dollar;
}
}
}
class Utility extends Property {
protected function calcRent() {
switch ($this->game->utilityCount($this->owner)) {
case 1: return new Dollar(4*$this->game->lastRoll());
case 2: return new Dollar(10*$this->game->lastRoll());
default: return new Dollar;
}
}
}
每个子类都继承了Property类,而且包括它自己的protected ClacRent() 方法。随着所有的基础方法都被定义, 每个子类都被实例化了。
为了开始游戏, 所有的Monopoly道具必须被创建起来。因为这章是介绍工厂模式的,所有Property的类型存在很多共性,你应该想到多态性,从而建立所有需要的对象。
我们还是以道具工厂类开始。 在我住的地方,政府的Assessor(定税人)掌握了税务和契约, 因此我命名它为的道具定税工厂。下一步,这个工厂将制造全部的专有道具。在真正应用时,所有的Monopoly道具的数值可能都取自于一个数据库或者一个文本, 但是对于这一个例子来说, 可以仅仅用一个数组来代替:
class Assessor {
protected $prop_info = array(
// streets
‘Mediterranean Ave.’ => array(‘Street’, 60, ‘Purple’, 2)
,’Baltic Ave.’ => array(‘Street’, 60, ‘Purple’, 2)
//more of the streets...
,’Boardwalk’ => array(‘Street’, 400, ‘Blue’, 50)
// railroads
,’Short Line R.R.’ => array(‘RailRoad’, 200)
//the rest of the railroads...
// utilities
,’Electric Company’ => array(‘Utility’, 150)
,’Water Works’ => array(‘Utility’, 150)
);
}
Property子类需要实例化Monopoly道具。现在,我们只是简单的用一个函数定义实例化变量$game,那么再把它加入Assessor类好了。
class Assessor {
protected $game;
public function setGame($game) { $this->game = $game; }
protected $prop_info = array(/* ... */);
}
也许你会偏向于选择使用数据库记录数据,不会用数组, 因为有一大堆的参数不可避免地要被罗列。如果是这样的话,可以考虑使用" 引入叁数对象 " 进行重构。
注:重构-引入叁数对象
方法中如果有很多参数,常常变得很复杂,而且容易导致错误。你可以引入一个封装参数的对象来替代一大堆的参数。举例来说,“start date” and “end date” 叁数可以用一个 DateRange 对象一起代替。
在Monopoly这个例子中,这个参数对象应该是什么呢?PropertyInfo,怎样?它的目的是使每个道具参数数组引入 PropertyInfo 类的构造器中,然后返回一个新对象。目的就意味着设计, 依照 TDD, 那意味着一个测试情形。
分享:《PHP设计模式介绍》第一章 编程惯用法学习一门新的语言意味着要采用新的惯用法。这章将介绍或者可能重新强调一些惯用法。你会发现这些惯用法在你要在代码中实现设计模式时候是非常有用的。 在这里总结的许多编程惯用法都是很值得
- 相关链接:
- 教程说明:
PHP教程-《PHP设计模式介绍》第三章 工厂模式(6)。