设计模式 - 组合模式(Composite Pattern)
参考:http://terrylee.cnblogs.com/archive/2006/03/11/347919.html
简介
树状结构中,对于枝节点和叶节点,接口不同,客户端需要判断处理的节点类型。
场景
对于规模比较大的公司,其组织架构一般是:总公司,下面有分公司和直属部门(总公司的人事、财务、行政),分公司下面又有直属部门(分公司的人事、财务、行政)和支公司,支公司下面是各个部门。
在客户端程序中,需要判断返回对象的具体类型到底是公司还是部门。如果是公司,则需要递归处理。
这增加了客户端程序与复杂元素内部结构之间的依赖。组合模式可以解决这种问题。
模式定义
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
组合模式模糊了树型结构问题中简单元素和复杂元素的概念,客户程序可以跟处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
模式特点
组合模式包含四个角色:
- Component:对象声明接口,声明所有类共有的默认行为
- Composite:有子节点的节点,存储子节点并实现 Component 接口有关操作
- Leaf:叶节点,没有子节点,用于定义对象的基本行为
- Client:客户类
组合模式可以不提供父对象的管理方法,但必须提供子对象的管理方法(add、remove 等)。
根据所实现接口的区别,组合模式包括两种:
- 透明组合模式:叶节点和枝节点接口统一。在 Component 中声明所有用来管理子对象的方法(Add、Remove 等)。这样实现 Component 接口的所有子类都具备了管理子对象的方法。问题很明显,因为 Leaf 类本身不具备管理子对象的功能,所以实现的部分方法没有意义。
- 安全组合模式:叶节点和枝节点接口不统一。不在 Component 中声明用来管理子对象的方法,而是在 Composite 声明所有用来管理子类对象的方法。问题也很明显,叶节点和枝节点将不具有相同的接口,客户端需要进行判断。
优点
- 将客户代码与复杂的对象容器结构解耦,客户代码只和抽象接口发生依赖关系
- 透明组合模式的叶节点和枝节点有完全一致的行为接口
缺点
- 透明组合模式的叶节点中,部分接口是没有意义的
- 安全组合模式的叶节点和枝节点具有不同的接口,客户端调用需要做相应的判断
PHP 代码示例
<?php
abstract class Component {
protected $name;
public function __construct($name) {
$this->name = $name;
}
public abstract function Add(Component $c);
public abstract function Remove(Component $c);
public abstract function Display(int $depth);
}
class Composite extends Component {
private $children = array();
public function Add(Component $c) {
array_push($this->children, $c);
}
public function Remove(Component $c) {
array_pop($this->children);
}
public function Display(int $depth) {
echo '-' . $depth . ' ' . $this->name . "<br/>";
foreach ($this->children as $component) {
$component->Display($depth + 2);
}
}
}
class Leaf extends Component {
public function Add(Component $c) {
echo "Cannot add to a leaf" . "<br/>";
}
public function Remove(Component $c) {
echo "Cannot remove from a leaf" . "<br/>";
}
public function Display(int $depth) {
echo '-' . depth . $this->name . "<br/>";
}
}
echo '<pre>';
$root = new Composite("root");
$root->Add(new Leaf("Leaf A"));
$root->Add(new Leaf("Leaf B"));
$comp = new Composite("Composite X");
$comp->Add(new Leaf("Leaf XA"));
$comp->Add(new Leaf("Leaf XB"));
$root->Add($comp);
$comp2 = new Composite("Composite XY");
$comp2->Add(new Leaf("Leaf XYA"));
$comp2->Add(new Leaf("Leaf XYB"));
$comp->Add($comp2);
$root->Add(new Leaf("Leaf C"));
print_r($root);
$leaf = new Leaf("Leaf D");
$root->Add($leaf);
$root->Remove($leaf);
$root->Display(1);
输出:
Composite Object
(
[children:Composite:private] => Array
(
[0] => Leaf Object
(
[name:protected] => Leaf A
)
[1] => Leaf Object
(
[name:protected] => Leaf B
)
[2] => Composite Object
(
[children:Composite:private] => Array
(
[0] => Leaf Object
(
[name:protected] => Leaf XA
)
[1] => Leaf Object
(
[name:protected] => Leaf XB
)
[2] => Composite Object
(
[children:Composite:private] => Array
(
[0] => Leaf Object
(
[name:protected] => Leaf XYA
)
[1] => Leaf Object
(
[name:protected] => Leaf XYB
)
)
[name:protected] => Composite XY
)
)
[name:protected] => Composite X
)
[3] => Leaf Object
(
[name:protected] => Leaf C
)
)
[name:protected] => root
)
-1 root
-depthLeaf A
-depthLeaf B
-3 Composite X
-depthLeaf XA
-depthLeaf XB
-5 Composite XY
-depthLeaf XYA
-depthLeaf XYB
-depthLeaf C