组合模式

问题:
在某些场景下,对于某些类而言,一个独立对象和多个对象组成的集合是没有差别的,即,一个独立对象支持的操作,多个对象组成的集合整体也能支持。
当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑使用组合模式了。

实现:
1. 类图示例:

2. 代码示例:

//单个对象基类
abstract class Unit
{
	//判断是否为对象集合,从而决定是否支持addUnit()和removeUnit()操作
	public function getComposite()
	{
		return null;
	}
	abstract public function bombardStrength();
}
//对象组合基类
abstract class CompositeUnit extends Unit
{
	private $units = [];
	public function getComposite()
	{
		return $this;
	}
	protected function units()
	{
		return $this->units;
	}
	public function removeUnit(Unit $unit)
	{
		$this->units = array_udiff($this->units, array($unit),
			function($a, $b) { return $a == $b ? 0 : 1; });
	}
	public function addUnit(Unit $unit)
	{
		if(in_array($unit, $this->units, true)) {
			return;
		}
		$this->units[] = $unit;
	}
}
//单个对象具体实现
class Archer extends Unit
{
	public function bombardStrength()
	{
		return 4;
	}
}
class LaserCanon extends Unit
{
	public function bombardStrength()
	{
		return 44;
	}
}
//对象组合具体实现
class TroopCarrier extends CompositeUnit
{
	public function bombardStrength()
	{
		$ret = 0;
		foreach($this->units() as $unit) {
			$ret += $unit->bombardStrength();
		}
		return $ret;
	}
}
class Army extends CompositeUnit
{
	public function bombardStrength()
	{
		$ret = 0;
		foreach($this->units() as $unit) {
			$ret += $unit->bombardStrength();
		}
		return $ret;
	}
}
//应用
class UnitScript
{
	static function joinExisting(Unit $newUnit, Unit $occupyingUnit) 
	{
		$comp;
		if(!is_null($comp = $occupyingUnit->getComposite())) {
			$comp->addUnit($newUnit);
		} else {
			$comp = new Army();
			$comp->addUnit($occupyingUnit);
			$comp->addUnit($newUnit);
		}
		return $comp;
	}
}
$obj = UnitScript::joinExisting(new Archer(), new LaserCanon());
var_dump($obj);
echo $obj->bombardStrength();

效果:
优点:
1. 组合模式中的组合类和局部类都继承自一个共同的基类,支持一个共同的操作集,这样对于组合结构的操作的复杂性都被完全隐藏了,对于客户端来说,操作组合结构与操作一个单独对象一样的简单。
缺点:
1. 简化的前提是使所有的类都继承同一个基类,简化的好处有时会以降低对象类型安全为代价。
2. 若组合结构中出现某些类不支持某些操作(例如示例中的Archer类和LaserCanon类就不支持addUnit()和removeUnit()操作),则需要手动进行类型检查,做出差异化,当这样的情况越来越多的时候,组合模式就开始显得利大于弊了。只有在大部分局部对象可互换的情况下,组合模式才是最适用的。
3. 组合结构的操作成本可能会是昂贵的。调用组合操作方法,会逐级调用对象树的下级分支的方法(例如示例中的bombardStrength()方法),若下级分支太多,可能会导致系统崩溃。解决办法之一是,在父级对象中缓存计算结果,但需要保证缓存值不会失效,这意味着需要设计一个当对树进行操作时可移除过期缓存的策略,而这也许需要给子对象加上对父对象的引用。
4. 组合模式很难将数据结构存储在关系型数据库中,但非常适合持久化为XML。

组合模式的两种实现方式:
1. 透明方式:所有子类具备完全一致的行为接口,对于外界没有区别(不支持某些操作的子类也需要实现这些操作,只不过可能只是实现抛出异常,没有什么意义)。
2. 安全方式:不支持某些操作的子类则不去实现这些操作,但是客户端的调用需要做相应的判断(代码示例中实现的就是安全方式)。

原文地址:https://www.cnblogs.com/wujuntian/p/9623344.html