設計模式(七)組合模式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) ? 定義了包含基本對象和組合對象的類層次結構 基本對象可以被組合成更復雜的組合對象,而這個組合對象又可以被組合,這樣不斷的遞歸下去。客戶代碼中,任何用到   基本對象的地方都可以使用組合對象。
     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
下一篇 2015-07-01

相關推薦

  • 7 文件系統權限(二):權限

    rwx, chmod, chgrp, chown, SUID, SGID, Sticky, chattr, lsattr, umask, ACL, setfacl, getfacl 權限 假設這樣幾個場景:     1) A用戶在/testdir目錄中創建了A.txt文件,B用戶是否可以刪除、修改、移動、重命…

    Linux干貨 2016-08-19
  • Linux網絡管理

                          Linux網絡管理   本章內容: 網絡概念 OSI七層模型 網絡設備 TCP/IP IP地址 配置網絡 實現網絡組 測試網絡 網絡工具   網絡用戶應用程序: WEB瀏覽器(…

    系統運維 2016-09-12
  • # Linux運維入門初步—–第二周

    標簽(空格分隔): Linux入門 linux 運維 — 寫作:N27_李偉 2017-7-13一、 Linux上常用的文件管理命令都有哪些,使用方法1. 文件類型的分類文件類型:– 普通文件類型d:目錄文件b:塊設備c:字符設備l:符號鏈接文件p:管道文件,pipes:套接字文件,socket例如: [root@loong ~]# …

    Linux干貨 2017-07-19
  • 遷移home到獨立分區

    1,fdisk ?/dev/sda ?創建10G的分區 Lsblk?查看下新創建分區是否同步 partx ?-a ?/dev/sda 同步 2,mkfs.ext4 ?/dev/sda6? -L? /home 創建文件系統跟卷標 3,mkdir ?/mnt/home ?創建一個掛載點 mount ?/dev/sda6??/mnt/home 掛載 4,cp? -…

    2017-12-14
  • 文件權限

    文件權限 文件屬性       rw-r–r–表示文件權限     Root 表示文件所有者     Root 表示文件所屬組     0 表示文件的大小 …

    Linux干貨 2016-08-11
  • Linux網絡屬性管理(二)

    Linux網絡屬性(二) Linux 網絡屬性管理(二) ip命令 ip – show / manipulate routing, devices, policy routing and tunnels ip [ OPTIONS …

    Linux干貨 2016-07-07
欧美性久久久久