設計模式(七)組合模式Composite(結構型)
1. 概述
在資料結構裡面,樹結構是很重要,我們可以把樹的結構應用到設計模式裡面。
例子1:就是多級樹形菜單。
例子2:檔案和檔案夾目錄
2.問題
我們可以使用簡單的對象組合成複雜的對象,而這個複雜物件有可以組合成更大的對象。我們可以把簡單這些對象定義成類,然後定義一些容器類來儲存這些簡單對象。用戶端代碼必須區別對象簡單對象和容器物件,而實際上大多數情況下使用者認為它們是一樣的。對這些類區別使用,使得程式更加複雜。遞迴使用的時候跟麻煩,而我們如何使用遞迴組合,使得使用者不必對這些類進行區別呢?
3. 解決方案
組合模式:將對象組合成樹形結構以表示“部分-整體”的階層。Composite使得使用者對單個對象和組合對象的使用具有一致性。
有時候又叫做部分-整體模式,它使我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程式可以向處理簡單元素一樣來處理複雜元素,從而使得客戶程式與複雜元素的內部結構解耦。
組合模式讓你可以最佳化處理遞迴或分級資料結構。有許多關於分級資料結構的例子,使得組合模式非常有用武之地。關於分級資料結構的一個普遍性的例子是你每次使用電腦時所遇到的:檔案系統。檔案系統由目錄和檔案組成。每個目錄都可以裝內容。目錄的內容可以是檔案,也可以是目錄。按照這種方式,電腦的檔案系統就是以遞迴結構來組織的。如果你想要描述這樣的資料結構,那麼你可以使用組合模式Composite。
4. 組合模式的分類
1) 將管理子項目的方法定義在Composite類中
2) 將管理子項目的方法定義在Component介面中,這樣Leaf類就需要對這些方法空實現。
5. 適用性
以下情況下適用Composite模式:
1).你想表示對象的部分-整體階層
2).你希望使用者忽略組合對象與單個對象的不同,使用者將統一地使用組合結構中的所有對象。
6.
結構
典型的C
o m p o s i t e對象結構如所示:
7. 構建模式的組成
抽象構件角色(component):是組合中的對象聲明介面,在適當的情況下,實現所有類共有介面的預設行為。聲明一個介面用於訪問和管理Component子組件。
這個介面可 以用來管理所有的子物件。(可選)在遞迴結構中定義一個介面,用於訪問一個父組件,並在合適的情況下實現它。
樹葉構件角色(Leaf):在組合樹中表示分葉節點對象,分葉節點沒有子節點。並在組合中定義圖元對象的行為。
樹枝構件角色(Composite):定義有子組件的那些組件的行為。儲存子組件。在Component介面中實現與子組件有關的操作。
客戶角色(Client):通過component介面操縱組合組件的對象。
8. 效果
1) • 定義了包含基本對象和組合對象的類階層 基本對象可以被組合成更複雜的組合對象,而這個組合對象又可以被組合,這樣不斷的遞迴下去。客戶代碼中,任何用到 基本對象的地方都可以使用組合對象。
2) • 簡化客戶代碼 客戶可以一致地使用組合結構和單個對象。通常使用者不知道 (也不關心)處理的是一個分葉節點還是一個組合組件。這就簡化了客戶代碼 , 因為在定義組合的那些類中不需要寫一些充斥著選擇語句的函數。
3) • 使得更容易增加新類型的組件 新定義的Composite或Leaf子類自動地與已有的結構和客戶代碼一起工作,客戶程式不需因新的Component類而改變。
4) • 使你的設計變得更加一般化 容易增加新組件也會產生一些問題,那就是很難限制組合中的組件。有時你希望一個組合只能有某些特定的組件。使用Composite時,你不能依賴類型系統施加這些約束,而必須在運行時刻進行檢查。
9. 實現
比較經典的例子是樹形菜單。多級展示,這個菜單可以無限增加節點;例外就是檔案遍曆等等。
<?php /** * 組合模式 * * @author guisu * @version 1.0 * 組合模式:樹形菜單 * * 將對象組合成樹形結構以表示"部分-整體"的階層,使得客戶對單個對象和綜合物件的使用具有一致性 */ /** * 抽象構件角色(component) * */abstract class MenuComponent{public function add($component){}public function remove($component){}public function getName(){}public function getUrl(){}public function displayOperation(){}}/** * 樹枝構件角色(Composite) * */class MenuComposite extends MenuComponent{private $_items = array();private $_name = null;private $_align = '';public function __construct($name) {$this->_name = $name;}public function add($component) {$this->_items[$component->getName()] = $component;}public function remove($component) {$key = array_search($component,$this->_items);if($key !== false) unset($this->_items[$key]);}public function getItems() {return $this->_items;}public function displayOperation() {static $align = '|';if($this->getItems()) {//substr($align, strpos($align,));$align .= ' _ _ ';}else{$align .='';}echo $this->_name, " <br/>";foreach($this->_items as $name=> $item) {echo $align;$item->displayOperation();}}public function getName(){return $this->_name;}}/** *樹葉構件角色(Leaf) * */class ItemLeaf extends MenuComponent{private $_name = null;private $_url = null;//public $_align = '----';public function __construct($name,$url){$this->_name = $name;$this->_url = $url;}public function displayOperation(){echo '<a href="', $this->_url, '">' , $this->_name, '</a><br/>';}public function getName(){return $this->_name;}}class Client{public static function displayMenu(){$subMenu1 = new MenuComposite("submenu1");$subMenu2 = new MenuComposite("submenu2");$subMenu3 = new MenuComposite("submenu3");$subMenu4 = new MenuComposite("submenu4");$subMenu5 = new MenuComposite("submenu5");/*$item1 = new ItemLeaf("sohu","www.163.com");$item2 = new ItemLeaf("sina","www.sina.com");$subMenu4 = new MenuComposite("submenu4");$subMenu1->add($subMenu4);$subMenu4->add($item1);$subMenu4->add($item2);*/$item3 = new ItemLeaf("baidu","www.baidu.com");$item4 = new ItemLeaf("google","www.google.com");$subMenu2->add($item3);$subMenu2->add($item4);$allMenu = new MenuComposite("AllMenu");$allMenu->add($subMenu1);$allMenu->add($subMenu2);$allMenu->add($subMenu3);$subMenu3->add($subMenu4);$subMenu4->add($subMenu5);$allMenu->displayOperation();}}// 建立menuClient::displayMenu();?>
10. 組合模式和其他相關模式
1)裝飾模式(Decorator模式)經常與Composite模式一起使用。當裝飾和組合一起使用時,它們
通常有一個公用的父類。因此裝飾必須支援具有 Add、Remove和GetChild 操作的Component介面。
2)Flyweight模式讓你共用組件,但不再能引用他們的父組件。
3)(迭代器模式)Itertor可用來遍曆Composite。
4)(觀察者模式)Visitor將本來應該分布在Composite和L e a f類中的操作和行為局部化。
11. 總結
組合模式解耦了客戶程式與複雜元素內部結構,從而使客戶程式可以向處理簡單元素一樣來處理複雜元素。
如果你想要建立階層,並可以在其中以相同的方式對待所有元素,那麼組合模式就是最理想的選擇。