設計模式-組合模式(Composite)

来源:http://www.cnblogs.com/bateman6988/archive/2017/12/09/7922907.html
-Advertisement-
Play Games

一、概念 將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。 二、模式動機 組合模式,通過設計一個抽像的組件類,使它既代表葉子對象,又代表組合對象,將葉子對象和組合對象統一起來。使得客戶端在操作時不再區分當前操作的是葉子對象還是組合對象,而是以 ...


一、概念

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

二、模式動機

   組合模式,通過設計一個抽像的組件類,使它既代表葉子對象,又代表組合對象,將葉子對象和組合對象統一起來。使得客戶端在操作時不再區分當前操作的是葉子對象還是組合對象,而是以一個統一方式來操作。

三、模式的結構

   

 

  示意代碼如下:

  

package composite.structure;

/**
 * 統一葉子和組件的抽象構件類
* @ClassName: Component 
* @author beteman6988
* @date 2017年11月29日 下午10:48:18 
*
 */
public abstract class Component {


    /**
     * 葉子和樹技構件都具備的商業邏輯
    * @Title: sampleOperation 
    * @param    
    * @return void    
    * @throws
     */
    public abstract void sampleOperation();

    /**
     * 向組合對象中添加組件對象
    * @Title: addChild 
    * @param @param child   
    * @return void    
    * @throws
     */
    public abstract void addChild(Component child);
    
    /**
     *  向組合對象中刪除組件對象
     * @param child
     * @roseuid 5A1EC291037D
     */
    public abstract void removeChild(Component child) ;

    /**
     * 從組合對象中取出第index個位置的組件對象
     * @param index
     * @return Component
     * @roseuid 5A1EC2F2011B
     */
    public abstract Component getChildren(Integer index) ;
}
package composite.structure;

import java.util.ArrayList;
import java.util.List;

/**
 * 組合對像,通常存儲子組件對象,實現抽象組件裡面定義的與操作子組件對象有關的行為。
* @ClassName: Composite 
* @author beteman6988
* @date 2017年12月3日 上午9:22:08 
*
 */
public class Composite extends Component {

    List<Component> childComponent=new ArrayList<Component>();
    
    /**
     * 商業邏輯,這裡樣例迭代所有子組件的該方法
     */
    @Override
    public void sampleOperation() {
        for(Component component:childComponent) {
            component.sampleOperation();
        }

    }
    
    @Override
    public void addChild(Component child)  {
        this.childComponent.add(child);
    }
    
    @Override
    public void removeChild(Component child)  {
        this.childComponent.remove(child);
    }
    
    @Override
    public Component getChildren(Integer index)  {
        if(!childComponent.isEmpty() && childComponent.size()>index ) {
            return this.childComponent.get(index);
        }
        return null;
    }
    

}
package composite.structure;

/**
 * 葉子組件對象
* @ClassName: Leaf 
* @author beteman6988
* @date 2017年12月3日 上午9:25:42 
*
 */
public class Leaf extends Component {

    /**
     * 商業邏輯
     */
    @Override
    public void sampleOperation() {
        //具體的商業邏輯
    }

    
    /**
     * 由於是葉子對象節點,不具備增加組件對象的功能,所以可以提供平庸實現
     */
    @Override
    public void addChild(Component child) {
        
    }

    /**
     * 由於是葉子對象節點,不具備刪除組件對象的功能,所以可以提供平庸實現
     */
    @Override
    public void removeChild(Component child) {
        // TODO Auto-generated method stub
        
    }

    /**
     * 由於是葉子對象節點,不具獲取子組件對象的功能,所以可以提供平庸實現
     */
    @Override
    public Component getChildren(Integer index) {
        // TODO Auto-generated method stub
        return null;
    }

}

        

   角色說明:

      Component:抽象的組件對象,可以是一個抽象類或者是一個介面,它既代表葉子對象,也代組合對象,讓客戶端通過這個介面來管理整個對象結構,上面的樣例代碼定義的是一個抽象類,且管理組合對象的方法是抽象的,其實也可以提供預設的實現。不提供預設實現的好處就是葉子對象針對這些不支持的行為必須給出明確的處理方式。提供預設形為的好處,如果預設的是針對葉子對象的行為,那麼針對葉子對象就不需要對這些不支持的形為提供明確的處理方式。並沒有說哪種就好哪種就劣,各有優缺。

      Composite:組合對象,通常存儲子組件,並包含操作子組件的具體行為,如對子組件進行增刪改查。

      Leaf:葉子節點組件:定義和實現與葉子對象的行為,不能包含其它子組件。

 

  在組合模式中,對象分為兩種,一為Composite對象,它可以包含其它的Composite或葉子對象,就像一個容器,二為不能包含子組件的Leaf對象。對於Composite對像,為了管理子組件對象,就要定以一些管理子組件的操作行為,以上的例 子中將這些操作形為定義在了抽象的Component組件中,但是這些行為其實對於Leaf對象是沒有意義的,這些行為可以放在Composite中,而非放在在Component中。所以跟據Composite對象中管理子組件的行為操作是放在Componnet或是Composite中,細分為了“透明模式”和“安全模式”

  透明模式:上面的示例結構為透明模式,透明模式對於客戶端來說,Leaf對象和Composite對象具有相同的行為,對於客戶端而言,Leaf對角和Composite對象是透明的,他們都是Component對象,具有相同的介面行為,也可以在Leaf對象上使用增加子組件的行為,只是這些行為對於Leaf對象是無意義的,這也意為著是不安全的。

       安全模式:針對透明模式中,管理子組件的行為如果不定義在Component中,而是定義在Composite中,這樣對於Leaf對象就是安全的了。因為客戶端對於Leaf對象,就無在調用管理子組件的行為。模式結構如下:

  

 

   示意代碼如下:

  

package composite.structure.safety;

/**
 * 統一葉子和組件的抽象構件類
* @ClassName: Component 
* @author beteman6988
* @date 2017年11月29日 下午10:48:18 
*
 */
public abstract class Component {


    /**
     * 葉子和樹技構件都具備的商業邏輯
    * @Title: sampleOperation 
    * @param    
    * @return void    
    * @throws
     */
    public abstract void sampleOperation();

}
package composite.structure.safety;

import java.util.ArrayList;
import java.util.List;

/**
 * 組合對像,通常存儲子組件對象,實現抽象組件裡面定義的與操作子組件對象有關的行為。
* @ClassName: Composite 
* @author beteman6988
* @date 2017年12月3日 上午9:22:08 
*
 */
public class Composite extends Component {

    List<Component> childComponent=new ArrayList<Component>();
    
    /**
     * 商業邏輯,這裡樣例迭代所有子組件的該方法
     */
    @Override
    public void sampleOperation() {
        for(Component component:childComponent) {
            component.sampleOperation();
        }
    }

    /**
     * 向組合對象中添加組件對象
    * @Title: addChild 
    * @param @param child   
    * @return void    
    * @throws
     */
    public void addChild(Component child)  {
        this.childComponent.add(child);
    }

    /**
     * 向組合對象中刪除組件對象
    * @Title: removeChild 
    * @param @param child   
    * @return void    
    * @throws
     */
    public void removeChild(Component child)  {
        this.childComponent.remove(child);
    }

    /**
     * 從組合對象中取出第index個位置的組件對象
    * @Title: getChildren 
    * @param @param index
    * @param @return   
    * @return Component    
    * @throws
     */
    public Component getChildren(Integer index)  {
        if(!childComponent.isEmpty() && childComponent.size()>index ) {
            return this.childComponent.get(index);
        }
        return null;
    }
    

}
package composite.structure.safety;

/**
 * 葉子組件對象
* @ClassName: Leaf 
* @author beteman6988
* @date 2017年12月3日 上午9:25:42 
*
 */
public class Leaf extends Component {

    /**
     * 商業邏輯
     */
    @Override
    public void sampleOperation() {
        //具體的商業邏輯
    }

}

 

 

四、模式樣例

  列印出以常見學的學校組織架構為例,如下:

      XX大學

         信息工程學院

      電腦科學系

      電子與信息工程系

    建築工程學院

      土木建築系

      工程管理系

    透明模式:

      

 

      

package composite.sample;

/**
 * 院系抽像介面
* @ClassName: SCCompent 
* @author beteman6988
* @date 2017年12月9日 下午8:15:46 
*
 */
public interface SCComponent {
    
    /**
     * get院系名稱
    * @Title: getName 
    * @param @return   
    * @return String    
    * @throws
     */
    public String getName();
    
    /**
     * 設置院系名稱
    * @Title: setName 
    * @param @return   
    * @return     
    * @throws
     */
    public void setName(String name);
    
    /**
     * 添加下級院系
    * @Title: addSCc 
    * @param @param scComponent   
    * @return void    
    * @throws
     */
    public void addSCc(SCComponent scComponent);
    
    /**
     * 移去下級院系
    * @Title: removeSCc 
    * @param @param scComponent   
    * @return void    
    * @throws
     */
    public void removeSCc(SCComponent scComponent);
    
    /**
     * 列印院系名稱
    * @Title: printName 
    * @param @param preStr   
    * @return void    
    * @throws
     */
    public void printName(String preStr);

}
package composite.sample;

/**
 * 葉子系節點
* @ClassName: Department 
* @author beteman6988
* @date 2017年12月9日 下午8:25:53 
*
 */
public class Department implements SCComponent {

    private String departmentName;
    
    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return departmentName;
    }

    @Override
    public void setName(String name) {
        this.departmentName=name;
    }

    @Override
    public void addSCc(SCComponent scComponent) {
        System.out.println("Department不支持addSCc行為!");
    }

    @Override
    public void removeSCc(SCComponent scComponent) {
        System.out.println("Department不支持removeSCc行為!");

    }
    
    @Override
    public void printName(String preStr) {
        System.out.println(preStr+this.departmentName);
    }

}
package composite.sample;

import java.util.ArrayList;
import java.util.List;

/**
 *院系設置的組合對象
* @ClassName: SCComposite 
* @author beteman6988
* @date 2017年12月9日 下午8:37:44 
*
 */
public class SCComposite implements SCComponent {

    private String name;
    
    private List<SCComponent> sCCList=new ArrayList<SCComponent>();
    
    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void setName(String name) {
        this.name=name;
    }

    @Override
    public void addSCc(SCComponent scComponent) {
        if(sCCList.indexOf(scComponent)<0) {
            sCCList.add(scComponent);
        }

    }

    @Override
    public void removeSCc(SCComponent scComponent) {
        this.sCCList.remove(scComponent);

    }

    @Override
    public void printName(String preStr) {
        System.out.println(preStr+this.name);
        for(SCComponent component:sCCList) {
            component.printName(preStr+"      ");
        }
    }
}
package composite.sample;

public class Client {
    public static void main(String[] args) {
        SCComponent head=new SCComposite();
        head.setName("XX大學");
        
        SCComponent sCCOne=new SCComposite();
        sCCOne.setName("信息工程學院");
        
        SCComponent sCCTwo=new SCComposite();
        sCCTwo.setName("建築工程學院");
        
        head.addSCc(sCCOne);
        head.addSCc(sCCTwo);
        
        SCComponent dptOne=new Department();
        dptOne.setName("電腦科學系");
        
        SCComponent dptTwo=new Department();
        dptTwo.setName("電子與信息工程系");
        
        sCCOne.addSCc(dptOne);
        sCCOne.addSCc(dptTwo);
        
        SCComponent dptThree=new Department();
        dptThree.setName("土木建築系");
        
        SCComponent dptFour=new Department();
        dptFour.setName("工程管理系");
        
        sCCTwo.addSCc(dptThree);
        sCCTwo.addSCc(dptFour);
        
        //sCCTwo.removeSCc(dptThree);
        head.printName("");

    }

}

運行結果如下:

  XX大學

    信息工程學院
      電腦科學系
      電子與信息工程系
    建築工程學院
      工程管理系

     安全模式:

              

        代碼如下:

                

package composite.sample.safety;

/**
 * 院系抽像介面
* @ClassName: SCCompent 
* @author beteman6988
* @date 2017年12月9日 下午8:15:46 
*
 */
public interface SCComponent {
    
    /**
     * get院系名稱
    * @Title: getName 
    * @param @return   
    * @return String    
    * @throws
     */
    public String getName();
    
    /**
     * 設置院系名稱
    * @Title: setName 
    * @param @return   
    * @return     
    * @throws
     */
    public void setName(String name);
    
    
    /**
     * 列印院系名稱
    * @Title: printName 
    * @param @param preStr   
    * @return void    
    * @throws
     */
    public void printName(String preStr);

}
package composite.sample.safety;

/**
 * 葉子系節點
* @ClassName: Department 
* @author beteman6988
* @date 2017年12月9日 下午8:25:53 
*
 */
public class Department implements SCComponent {

    private String departmentName;
    
    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return departmentName;
    }

    @Override
    public void setName(String name) {
        this.departmentName=name;
    }

    
    @Override
    public void printName(String preStr) {
        System.out.println(preStr+this.departmentName);
    }

}
package composite.sample.safety;

import java.util.ArrayList;
import java.util.List;

/**
 *院系設置的組合對象
* @ClassName: SCComposite 
* @author beteman6988
* @date 2017年12月9日 下午8:37:44 
*
 */
public class SCComposite implements SCComponent {

    private String name;
    
    private List<SCComponent> sCCList=new ArrayList<SCComponent>();
    
    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void setName(String name) {
        this.name=name;
    }


    public void addSCc(SCComponent scComponent) {
        if(sCCList.indexOf(scComponent)<0) {
            sCCList.add(scComponent);
        }

    }


    public void removeSCc(SCComponent scComponent) {
        this.sCCList.remove(scComponent);

    }

    @Override
    public void printName(String preStr) {
        System.out.println(preStr+this.name);
        for(SCComponent component:sCCList) {
            component.printName(preStr+"      ");
        }
    }
}
package composite.sample.safety;

public class Client {
    public static void main(String[] args) {
        SCComposite head=new SCComposite();
        head.setName("XX大學");
        
        SCComposite sCCOne=new SCComposite();
        sCCOne.setName("信息工程學院");
        
        SCComposite sCCTwo=new SCComposite();
        sCCTwo.setName("建築工程學院");
        
        head.addSCc(sCCOne);
        head.addSCc(sCCTwo);
        
        SCComponent dptOne=new Department();
        dptOne.setName("電腦科學系");
        
        SCComponent dptTwo=new Department();
        dptTwo.setName("電子與信息工程系");
        
        sCCOne.addSCc(dptOne);
        sCCOne.addSCc(dptTwo);
        
        SCComponent dptThree=new Department();
        dptThree.setName("土木建築系");
        
        SCComponent dptFour=new Department();
        dptFour.setName("工程管理系");
        
        sCCTwo.addSCc(dptThree);
        sCCTwo.addSCc(dptFour);
        
        //sCCTwo.removeSCc(dptThree);
        head.printName("");

    }

}

運行結果如下:

XX大學
  信息工程學院
    電腦科學系
    電子與信息工程系
  建築工程學院
    土木建築系
    工程管理系

 五、模式優缺點

        優點:1.定義了包含基本對象和組合對象的類層次結構

                   2.統一了組合對象和葉子對象,在組合模式中,可以把葉子對象當成特殊的組合對象來看待,為他們定義統一的父類,從而把葉子對象和組合對象的行為統一起來。

                   3.方便擴展,只要是Component的子類對象,都可以很方便添加到組合對象中。

         缺點:優點中的第3點既是優點,也是缺點,組合對象很難確定裡面的具體組件類型,因為只要是Component的子類對象,都可以添加到組合對象中,所以當要確定組合對象中組件具體的類型時,就必須在運行期動態檢測。

            

           


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、介紹 本次博客主要介紹函數表達式的內容,主要是閉包。 二、函數表達式 定義函數的兩種方式:一個是函數聲明,另一個就是函數表達式。 區別: 1.函數聲明是用function後面有函數名,函數表達式是賦值形式給一個變數。 2.函數聲明可以提升函數,而函數表達式不會提升 函數提升就是函數會被自動提升到 ...
  • //多個值同時變化function getStyle(obj, name)//函數幫助獲取不在行間樣式,不受非行間border,padding等得影響{ //style只獲取行間樣式。offset受非行間border,padding等得影響 if(obj.currentStyle) { return ...
  • 1.JS事件的基本知識 JS事件的學習和JS方法的學習揉雜在一起,JS相對於Java等語言,方法定義和使用上比較隨意和簡單,但是還是有一些區別,需要理清楚. 2.jQuery方式綁定事件 這裡多多贅述一點,由於jQuery可以理解為是對JS的一種高級封裝,這種封裝是單向的,所以我們可以在JS中加入j ...
  • 地圖與地理定位 定位在大部分項目中都需要實現,如何實現主要有如下的幾種方法 1. H5定位 在HTML5中navigator有很強大的功能,其中就有定位的方法 這個服務其實是谷歌提供的,在我們國內使用的可能性較低 2. 後端定位 前端調用一個後端提供的介面,後端進行定位操作,返回給前端 在工作中公司 ...
  • 基礎數據結構與演算法 現在有兩個不同的JSON,比較複雜,可以參考這裡的DEMO中返回的JSON。要比較它們的差異,除了用現成的工具如beyond compare以外,如果我們的機器上沒有安裝這個工具,能如何較快解決?作為一個程式員,一個個對比是不可行的,對比完也不會有什麼收穫。我會把之放進Excel ...
  • 跨域問題的產生: 因為瀏覽器有同源策略,只有在同功能變數名稱,同埠,同協議的情況下才可以進行數據交互;有的時候,例如:在公司開發項目的時候,前端開發的伺服器可能和後端伺服器不是同一個,因為可能是通過gulp、webpack搭建的開發伺服器,就需要解決跨域問題,再例如,在大公司數據伺服器不只有一個,所以跨域 ...
  • 大整數的四則運算已經是老生常談的問題了。很多的庫也已經包含了各種各樣的解決方案。 作為練習,我們從最簡單的加減法開始。 加減法的核心思路是用倒序數組來模擬一個大數,然後將兩個大數的利用豎式進行運算。 加法函數: 異符號相加時調用減法函數(減法函數後面給出) 同符號相加先確定符號 因為輸入輸出的為字元 ...
  • 作者根據JavaScript紅皮書,結合簡單的測試,比較全面的總結了JavaScript中Array(數組)類型的相關知識。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...