設計模式(七)組合模式Composite(結構型)

1. 概述

在數據結構里面,樹結構是很重要,我們可以把樹的結構應用到設計模式里面。

例子1:就是多級樹形菜單。

例子2:文件和文件夾目錄

2.問題

我們可以使用簡單的對象組合成復雜的對象,而這個復雜對象有可以組合成更大的對象。我們可以把簡單這些對象定義成類,然后定義一些容器類來存儲這些簡單對象??蛻舳舜a必須區別對象簡單對象和容器對象,而實際上大多數情況下用戶認為它們是一樣的。對這些類區別使用,使得程序更加復雜。遞歸使用的時候跟麻煩,而我們如何使用遞歸組合,使得用戶不必對這些類進行區別呢?

3.   解決方案

        組合模式將對象組合成樹形結構以表示“部分-整體”的層次結構。Composite使得用戶對單個對象和組合對象的使用具有一致性。

        有時候又叫做部分-整體模式,它使我們樹型結構的問題中,模糊了簡單元素和復雜元素的概念,客戶程序可以向處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內部結構解耦。

  組合模式讓你可以優化處理遞歸或分級數據結構。有許多關于分級數據結構的例子,使得組合模式非常有用武之地。關于分級數據結構的一個普遍性的例子是你每次使用電腦時所遇到的:文件系統。文件系統由目錄和文件組成。每個目錄都可以裝內容。目錄的內容可以是文件,也可以是目錄。按照這種方式,計算機的文件系統就是以遞歸結構來組織的。如果你想要描述這樣的數據結構,那么你可以使用組合模式Composite。

4.  組合模式的分類

1)    將管理子元素的方法定義在Composite類中
2)    將管理子元素的方法定義在Component接口中,這樣Leaf類就需要對這些方法空實現。

5. 適用性

以下情況下適用Composite模式

1).你想表示對象的部分-整體層次結構

2).你希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象。

6.
結構

1.jpg

典型的C
o m p o s i t e對象結構如下圖所示:

2.jpg

7. 構建模式的組成

          抽象構件角色(component):是組合中的對象聲明接口,在適當的情況下,實現所有類共有接口的默認行為。聲明一個接口用于訪問和管理Component子部件。

          這個接口可  以用來管理所有的子對象。(可選)在遞歸結構中定義一個接口,用于訪問一個父部件,并在合適的情況下實現它。  

          樹葉構件角色(Leaf):在組合樹中表示葉節點對象,葉節點沒有子節點。并在組合中定義圖元對象的行為。
          樹枝構件角色(Composite):定義有子部件的那些部件的行為。存儲子部件。在Component接口中實現與子部件有關的操作。
          客戶角色(Client):通過component接口操縱組合部件的對象。   

8. 效果

     1) ? 定義了包含基本對象和組合對象的類層次結構 基本對象可以被組合成更復雜的組合對象,而這個組合對象又可以被組合,這樣不斷的遞歸下去??蛻舸a中,任何用到   基本對象的地方都可以使用組合對象。
     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();  
    }  
}  
  
// 創建menu  
Client::displayMenu();  
?>

10. 組合模式和其他相關模式

1)裝飾模式(Decorator模式)經常與Composite模式一起使用。當裝飾和組合一起使用時,它們

通常有一個公共的父類。因此裝飾必須支持具有 Add、Remove和GetChild 操作的Component接口。

2)Flyweight模式讓你共享組件,但不再能引用他們的父部件。

3)(迭代器模式)Itertor可用來遍歷Composite。

4)(觀察者模式)Visitor將本來應該分布在Composite和L e a f類中的操作和行為局部化。

11. 總結

組合模式解耦了客戶程序與復雜元素內部結構,從而使客戶程序可以向處理簡單元素一樣來處理復雜元素。

如果你想要創建層次結構,并可以在其中以相同的方式對待所有元素,那么組合模式就是最理想的選擇。

轉自:http://blog.csdn.net/hguisu/article/details/7530783

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2938

(0)
s19930811s19930811
上一篇 2015-06-30 17:02
下一篇 2015-07-01 10:19

相關推薦

  • Linux基礎(四)——用戶管理&grep命令

    1、  復制/etc/skel目錄為/home/tuser1,要求/home/tuser1及其內部文件的屬組和其它用戶均沒有任何訪問權限。 cp /etc/skel/ /home/tuser1 –r chmod -R 700 /home/tuser1/ 2、  編輯/etc/group文件,添加組hadoop。 echo "ha…

    Linux干貨 2016-11-08
  • 關于大型網站技術演進的思考(十一)–網站靜態化處理—動靜分離策略(3)

    原文出處: 夏天的森林   前文里我講到了網站靜態化的關鍵點是動靜分離,動靜分離是讓動態網站里的動態網頁根據一定規則把不變的資源和經常變的資源區分開來,動靜資源做好了拆分以后,我們就可以根據靜態資源的特點將其做緩存操作,這就是網站靜態化處理的核心思路。由此可見,網站靜態化處理的核心就是動靜分離和緩存兩大方面,上篇我簡單講述了動靜…

    Linux干貨 2015-03-11
  • 文本處理工具及grep命令的理解與使用

    1. 文本處理工具   1.1 文件查看命令cat tac     cat命令:連接文件并在標準輸出上面顯示出來  圖 1-1   cat幫助文檔        cat [option]…[file]…     &n…

    Linux干貨 2016-08-07
  • Linux Basics–part5

    1、顯示當前系統上root、fedora或user1用戶的默認shell ~]# grep “^\(root\|fedora\|user1\)” /etc/passwd | cut -d: -f7 或者 ~]# egrep “^(root|fedora|user1)” /etc/passwd | cut -d:…

    Linux干貨 2017-08-24
  • ubuntu kylin虛擬機安裝KVM

    1.確定物理機CPU是否支持虛擬化    查看方法1:計算機->屬性->處理器(Inter(R) Core(TM) i5-2450M),然后去網上查看自己的電腦CPU是否支持虛擬化,    地址http://ark.intel.com/zh-cn/找到自己CPU的信息,虛擬化技術后是否顯示為yes,如果是n…

    Linux干貨 2016-10-15
  • 第五周學習總結-rpm&yum

    我們都知道,計算機只能識別二進制程序,而程序員編寫的源代碼都是以純文本形式存在,因此,要想讓計算機識別并運行這些源代碼程序,就必須通過中間的轉換機制讓源代碼變為二進制程序文件,而這種轉換過程就稱為編譯過程。Linux的各發行版本中由于各廠商的編譯過程不盡相同,因此就誕生了各種不同的軟件管理包組件。其中我們最熟知的就要數Redhat系列的rpm包了。 rpm包…

    2018-01-03
欧美性久久久久