1、组合模式
这个我觉得书上的例子很不错
游戏中的战斗单元,单元可以组合成强大的部队,一起移动、战斗和防守(表示组合后的东西,跟原来的单个东西,有相同的接口,即相同的使用方式。你只要直接调用move(),不管他是单个步兵,还是一个连),总结起来就是,单兵和部队都继承自战斗单元,部队又组合了很多单兵。
开始,一个简单的
<?php
abstract class Unit
{
// 返回该作战单元的战斗力
abstract public function bombardStrength();
}
class Archer extends Unit
{
public function bombardStrength()
{
return 4;
}
}
class LaserCannonUnit extends Unit
{
public function bombardStrength()
{
return 44;
}
}
// 上面两个为单兵,下面开始把他们组合成一支军队
class Army
{
private $units = [];
public function addUnit(Unit $unit)
{
array_push($this->units, $unit);
}
public function bombardStrength()
{
$ret = 0;
foreach ($this->units as $unit) {
$ret += $unit->bombardStrength();
}
return $ret;
}
}
$army = new Army();
$army->addUnit(new Archer());
$army->addUnit(new LaserCannonUnit());
echo $army->bombardStrength();
更详细的看书吧,关于如何一步步优化这个例子!最终的结果是,Army
和TroopCarrier
继承自CompositeUnit
,而CompositeUnit
继承自Unit
,CompositeUnit
类,有自己扩展的addUnit
和removeUnit
方法。
2、装饰模式
书上的例子是,一个地表类Plains
,继承自区域类Tile
,然后有钻石地表类DiamondPlains
和污染地表类PollutedPlains
继承自Plains
类。
现在我们需要一个既有钻石,又受到污染的地表,类,当然,首先立马想到的就是组合,出个组合类
PollutedDiamondPlains
。
然而,书上的例子,看上去似乎可以这样。但是如果加上更多的地表类型,比如湿地地表类、草皮地表、干涸地表等,有些是可以随意组合的,这样类就会爆炸式增长。
现在就需要装饰模式了。
<?php
abstract class Tile
{
abstract public function getWealthFactor();
}
class Plains extends Tile
{
private $wealth_factor = 2;
public function getWealthFactor()
{
return $this->wealth_factor;
}
}
abstract class TileDecorator extends Tile
{
protected $tile;
public function __construct(Tile $tile)
{
$this->tile = $tile;
}
}
class DiamondDecorator extends TileDecorator
{
function getWealthFactor()
{
return $this->tile->getWealthFactor() + 2;
}
}
class PollutionDecorator extends TileDecorator
{
function getWealthFactor()
{
return $this->tile->getWealthFactor() - 4;
}
}
$tile = new Plains();
echo $tile->getWealthFactor(), PHP_EOL; // 2
// 相当于,任你自由组合,这里是把钻石和地表组合
$tile = new DiamondDecorator(new Plains());
echo $tile->getWealthFactor(), PHP_EOL; // 4
// 这里是把污染和钻石和地表组合
$tile = new PollutionDecorator(new DiamondDecorator(new Plains()));
echo $tile->getWealthFactor(), PHP_EOL; // 0
比如,我现在加入一个WetDecorator
类,就可以组合出钻石湿地、污染湿地、钻石污染湿地来了,靠组合模式的话,得添加3个类。。
3、外观模式(Facade,或者叫门面模式)
自我理解:外观模式隐藏一系列相互关联的操作到类中,并放出适当的接口(方法)以供调用并返回相应的数据。而不是建一堆变量,在一堆函数种传来传去。
书中的例子:
<?php
// 目的:解析这样的数据"234-ladies_jumper 55"
// 普通过程式
function get_product_file_lines($file)
{
return file($file);
}
function get_product_object_from_id($id, $product_name)
{
return new Product($id, $product_name);
}
function get_name_from_line($line)
{
if (preg_match("/.*-(.*)\s\d+/", $line, $array)) {
return str_replace('_', ' ', $array[1]);
}
return '';
}
function get_id_from_line($line)
{
if (preg_match("/^(\d{1,3})-/", $line, $array)) {
return $array[1];
}
return -1;
}
class Product
{
public $id;
public $name;
function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}
// 测试
$lines = get_product_file_lines('test.txt');
$objects = [];
foreach ($lines as $line) {
$id = get_id_from_line($line);
$name = get_name_from_line($line);
$objects[$id] = get_product_object_from_id($id, $name);
}
如果你做出来的东西,给别人用,别人还要这样使用的话,估计会打死你吧。
接下来用类包装一下
class ProductFacade
{
private $products = [];
function __construct($file)
{
$this->file = $file;
$this->compile();
}
private function compile()
{
$lines = get_product_file_lines($this->file);
foreach ($lines as $line) {
$id = get_id_from_line($line);
$name = get_name_from_line($line);
$this->products[$id] = get_product_object_from_id($id, $name);
}
}
public function getProducts()
{
return $this->products;
}
public function getProduct($id)
{
return $this->products[$id];
}
}
$facade = new ProductFacade('test.txt');
echo $facade->getProduct(234)->name;