參考: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 + );
}
}
}
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();
輸出:
Composite Object
(
[children:Composite:private] => Array
(
[] => Leaf Object
(
[name:protected] => Leaf A
)
[] => Leaf Object
(
[name:protected] => Leaf B
)
[] => Composite Object
(
[children:Composite:private] => Array
(
[] => Leaf Object
(
[name:protected] => Leaf XA
)
[] => Leaf Object
(
[name:protected] => Leaf XB
)
[] => Composite Object
(
[children:Composite:private] => Array
(
[] => Leaf Object
(
[name:protected] => Leaf XYA
)
[] => Leaf Object
(
[name:protected] => Leaf XYB
)
)
[name:protected] => Composite XY
)
)
[name:protected] => Composite X
)
[] => Leaf Object
(
[name:protected] => Leaf C
)
)
[name:protected] => root
)
- root
-depthLeaf A
-depthLeaf B
- Composite X
-depthLeaf XA
-depthLeaf XB
- Composite XY
-depthLeaf XYA
-depthLeaf XYB
-depthLeaf C