组合模式
将一组对象组合为可像单个对象一样被使用的结构。
组合模式定义了一个单根继承体系,使具有截然不同职责的集合可以并肩工作。组合模式中的类必须支持一个共同的操作集,以将其作为它们首要职责。类同时必须拥有添加和删除子对象的方法。
abstract class Unit
{
abstract function addUnit(Unit $unit);
abstract function removeUnit(Unit $unit);
abstract function bombardStrength();
}
class Army extends Unit
{
private $units = array();
function addUnit(Unit $unit)
{
if (in_array($unit, $this->units, true)) {
return;
}
$this->units[] = $unit;
}
function removeUnit(Unit $unit)
{
$this->units = array_udiff($this->units, array($unit), function ($a, $b) {
return ($a === $b) ? 0 : 1;
});
}
function bombardStrength()
{
$ret = 0;
foreach ($this->units as $unit) {
$ret += $unit->bombardStrength();
}
return $ret;
}
}
实际上,我们并不希望在Archer对中添加Unit对象,我们可以修改之前设计的代码,在Unit类的addUnit()/removeUnit()方法中抛出异常:
class UnitException extends Exception{};
abstract class Unit
{
function addUnit(Unit $unit){
throw new UnitException(get_class($this)." is a leaf");
}
function removeUnit(Unit $unit){
throw new UnitException(get_class($this)." is a leaf");
}
abstract function bombardStrength();
}
这样做可以出去局部类中的重复代码,但是同时组合类不再需要强制地实现和两个方法,这可能会带来问题。
我们先回顾一下组合模式带来的益处:
- 灵活:因为组合模式中的一切类型都共享一个父类型,所以可以轻松地在设计中添加新的组合对象或者局部对象,而无需大范围地修改代码。
- 简单:使用组合结构的客户端代码只需要设计简单的接口。
- 隐式到达:组合模式中的对象通过树形结构组织。
- 显式到达:树形结构可以轻松遍历。
我们将add/remove方法降到下一级对象中,以便这些方法仅在组合类中使用:
abstract class Unit
{
function getComposite()
{
return null;
}
abstract function bombardStrength();
}
abstract class CompositeUnit extends Unit
{
private $units = array();
function getComposite()
{
return $this;
}
protected function units()
{
return $this->units;
}
function addUnit(Unit $unit)
{
if (in_array($unit, $this->units, true)) {
return;
}
$this->units[] = $unit;
}
function removeUnit(Unit $unit)
{
$this->units = array_udiff($this->units, array($unit), function ($a, $b) {
return ($a === $b) ? 0 : 1;
});
}
}