你还记得“组合优于继承”这个设计原则吗?该原则体现了类和对象之间的组织弹性。为了使项目更具灵活性,我们需要将类按一定结构组织起来,以便它们的对象在代码运行时能被构造为有用的结构。
下面我们来介绍组合模式,将一组对象组合为可像单一对象一样被使用的结构,这是组合模式的原则。好比是一棵树,有枝有叶,但是你使用的时候拿起的是一条有枝有叶的枝干。组合模式便是枝叶组合的过程。
例子是这样的,一个很老的例子。玩家可以在地图上组成战斗单元(Unit),战斗单元上有弓箭手(Archer)和激光炮(LaserCannonUnit),战斗单元能组成独立战斗单元-军队(Army)。独立战斗单元之间能够合并新的战斗单元,而战斗单元只能添加。
<?php
/**
* 基础单元
* Class Unit
*/
abstract class Unit {
function getComposite() {
return null;
}
/**
* 获得战力
* @return mixed
*/
abstract function bombardStrength();
}
/**
* 上层单元
* Class CompositeUnit
*/
abstract class CompositeUnit extends Unit {
protected $units = array();
function getComposite() {
return $this;
}
protected function units() {
return $this->units;
}
function removeUnit( Unit $unit ) {
$this->units = array_udiff( $this->units, array( $unit ),
function( $a, $b ) { return ($a === $b)?0:1; } );
}
function addUnit( Unit $unit ) {
if ( in_array( $unit, $this->units, true ) ) {
return;
}
$this->units[] = $unit;
}
}
/**
* 军队
* Class Army
*/
class Army extends CompositeUnit {
function bombardStrength() {
$ret = 0;
foreach( $this->units as $unit ) {
$ret += $unit->bombardStrength();
}
return $ret;
}
}
/**
* 弓箭手
* Class Archer
*/
class Archer extends Unit {
function bombardStrength() {
return 4;
}
}
/**
* 激光炮
* Class LaserCannonUnit
*/
class LaserCannonUnit extends Unit {
function bombardStrength() {
return 44;
}
}
/**
* 军队合成器
* Class UnitScript
*/
class UnitScript {
static function joinExisting( Unit $newUnit, Unit $occupyingUnit ) {
if ( ! is_null( $comp = $occupyingUnit->getComposite() ) ) {
$comp->addUnit( $newUnit );
} else {
$comp = new Army();
$comp->addUnit( $occupyingUnit );
$comp->addUnit( $newUnit );
}
return $comp;
}
}
$army1 = new Army();
$army1->addUnit( new Archer() );
$army1->addUnit( new Archer() );
$army2 = new Army();
$army2->addUnit( new Archer() );
$army2->addUnit( new Archer() );
$army2->addUnit( new LaserCannonUnit() );
$composite = UnitScript::joinExisting( $army2, $army1 );
print_r( $composite->bombardStrength() );
如果你想像对待单个对象一样对待组合对象,那么组合模式十分有用。因为组合模式是树状的结构,所以对整体的操作会影响到局部,而通过组合,对客户端代码来说局部的数据有时透明的。