11.組合模式設計思想

杨充發表於2024-11-08

11.組合模式設計思想

目錄介紹
  • 01.組合模式基礎

    • 1.1 組合模式由來
    • 1.2 組合模式定義
    • 1.3 組合模式場景
    • 1.4 組合模式思考
    • 1.5 解決的問題
  • 02.組合模式實現

    • 2.1 羅列一個場景
    • 2.2 組合結構
    • 2.3 組合基本實現
    • 2.4 有哪些注意點
  • 03.組合例項演示

    • 3.1 需求分析
    • 3.2 程式碼案例實現
    • 3.3 是否可以最佳化
    • 3.4 組合設計
    • 3.5 演變程式碼案例
  • 04.組合實現方式

    • 4.1 組合模式分類
    • 4.2 案例分析
    • 4.3 透明式組合
    • 4.4 安全式組合
  • 05.組合模式分析

    • 5.1 組合模式優點
    • 5.2 組合模式缺點
    • 5.3 適用環境
    • 5.4 模式擴充
    • 5.5 使用建議說明
  • 06.外觀代理總結

    • 6.1 總結一下學習
    • 6.2 更多內容推薦

推薦一個好玩網站

一個最純粹的技術分享網站,打造精品技術程式設計專欄!程式設計進階網

https://yccoding.com/

01.組合模式基礎

1.0 本部落格AI摘要

本文詳細介紹了組合模式的設計思想和實現方法,涵蓋組合模式的基礎概念、實現步驟、例項演示、實現方式、優缺點分析等內容。透過具體的程式碼案例,展示瞭如何使用組合模式來處理具有層次結構的物件,如檔案系統和購物清單,使客戶端可以一致地處理單個物件和組合物件。文章還探討了透明式和安全式組合模式的區別,並提供了設計建議和適用場景。適合初學者和有一定經驗的開發者閱讀。

1.1 組合模式由來

客戶程式碼過多地依賴於物件容器複雜的內部實現結構,物件容器內部實現結構(而非抽象介面)的變化將引起客戶程式碼的頻繁變化,帶來了程式碼的維護性、擴充套件性等弊端。

如何將“客戶程式碼與複雜的物件容器結構”解耦?讓物件容器自己來實現自身的複雜結構,從而使得客戶程式碼就像處理簡單物件一樣來處理複雜的物件容器?

1.2 組合模式定義

定義:將物件以樹形結構組織起來,以達成“部分-整體”的層次結構,使得客戶端對單個物件和組合物件的使用具有一致性。

1.3 組合模式場景

使用場景

  1. 當需要表示物件的層次結構時,如檔案系統或組織結構。更多內容
  2. 當希望客戶端程式碼能夠以一致的方式處理樹形結構中的所有物件時。

1.4 組合模式思考

當我們思考組合模式時,以下幾個方面值得考慮:

  1. 物件的層次結構:組合模式適用於具有層次結構的物件集合。我們需要思考物件之間的關係,確定哪些物件可以作為容器物件,哪些物件可以作為葉子物件,以及它們之間的組合方式。
  2. 統一的介面:組合模式透過定義統一的介面,使得容器物件和葉子物件可以被一致地對待。我們需要思考如何設計這個介面,以便能夠在不同層次的物件上進行統一的操作。
  3. 遞迴結構:組合模式通常使用遞迴結構來處理物件的層次關係。我們需要思考如何在遞迴中遍歷和操作物件集合,以實現統一的操作和處理。
  4. 葉子物件的特殊處理:組合模式中的葉子物件可能需要特殊處理,因為它們沒有子物件。我們需要思考如何處理這些葉子物件,以確保它們能夠正確地參與到組合結構中。

1.5 解決的問題

主要解決的問題

  1. 簡化樹形結構中物件的處理,無論它們是單個物件還是組合物件。
  2. 解耦客戶端程式碼與複雜元素的內部結構,使得客戶端可以統一處理所有型別的節點。更多內容

02.組合模式實現

2.1 羅列一個場景

假設我們有一個檔案系統,其中有兩種型別的檔案:文字檔案和資料夾。文字檔案是葉子節點,資料夾是組合節點,可以包含其他檔案。我們想要使用組合模式來實現檔案系統的層次結構,並且提供一個列印檔案路徑的方法。

2.2 組合結構

組合模式包含如下角色:更多內容

  1. 元件(Component): 定義了組合中所有物件的通用介面,可以是抽象類或介面。它宣告瞭用於訪問和管理子元件的方法,包括新增、刪除、獲取子元件等。
  2. 葉子節點(Leaf): 表示組合中的葉子節點物件,葉子節點沒有子節點。它實現了元件介面的方法,但通常不包含子元件。
  3. 複合節點(Composite): 表示組合中的複合物件,複合節點可以包含子節點,可以是葉子節點,也可以是其他複合節點。它實現了元件介面的方法,包括管理子元件的方法。
  4. 客戶端(Client): 透過元件介面與組合結構進行互動,客戶端不需要區分葉子節點和複合節點,可以一致地對待整體和部分。

2.3 組合基本實現

定義抽象元件(Component)

public interface File {
    // 獲取檔名稱
    String getName();
    // 新增子檔案
    void add(File file);
    // 刪除子檔案
    void remove(File file);
    // 獲取子檔案
    List<File> getChildren();
    // 列印檔案路徑
    void printPath(int space);
}

定義葉子節點(Leaf)

public class TextFile implements File {
    private String name;

    public TextFile(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void add(File file) {
        throw new UnsupportedOperationException("Text file cannot add child file");
    }

    @Override
    public void remove(File file) {
        throw new UnsupportedOperationException("Text file cannot remove child file");
    }

    @Override
    public List<File> getChildren() {
        throw new UnsupportedOperationException("Text file has no child file");
    }

    @Override
    public void printPath(int space) {
        StringBuilder sp = new StringBuilder();
        for (int i = 0; i < space; i++) {
            sp.append(" ");
        }
        System.out.println(sp + name);
    }
}

定義組合節點

public class Folder implements File {
    private String name;
    private List<File> children;

    public Folder(String name) {
        this.name = name;
        children = new ArrayList<>();
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void add(File file) {
        children.add(file);
    }

    @Override
    public void remove(File file) {
        children.remove(file);
    }

    @Override
    public List<File> getChildren() {
        return children;
    }

    @Override
    public void printPath(int space) {
        StringBuilder sp = new StringBuilder();
        for (int i = 0; i < space; i++) {
            sp.append(" ");
        }
        System.out.println(sp + name);
        space += 2;
        for (File child : children) {
            child.printPath(space);
        }
    }
}

客戶端程式碼

private void test() {
    // 建立一個根資料夾,並新增兩個文字檔案和一個子資料夾
    File root = new Folder("root");
    root.add(new TextFile("a.txt"));
    root.add(new TextFile("b.txt"));
    File subFolder = new Folder("subFolder");
    root.add(subFolder);

    // 在子資料夾中新增兩個文字檔案
    subFolder.add(new TextFile("c.txt"));
    subFolder.add(new TextFile("d.txt"));

    // 列印根資料夾的路徑
    root.printPath(0);
}

列印結果如下所示:

root
  a.txt
  b.txt
  subFolder
    c.txt
    d.txt

2.4 有哪些注意點

在使用組合模式時,有幾個注意點需要考慮:

  1. 抽象構件的一致性:組合模式中,抽象構件定義了組合物件和葉子物件的共同介面。確保所有的元件都遵循相同的介面和行為,以保持一致性。
  2. 新增和刪除子節點的限制:在組合模式中,組合物件可以包含其他組合物件或葉子物件作為子節點。但是,有時可能需要限制新增或刪除子節點的操作,以確保組合物件的結構和約束條件得到維護。
  3. 葉子物件的操作限制:葉子物件是組合模式中的最小單位,它沒有子節點。因此,對於葉子物件的操作可能需要進行限制或者丟擲異常,以避免不必要的操作。
  4. 透明性和安全性的權衡:組合模式可以採用透明性或安全性的實現方式。在選擇實現方式時,需要權衡透明性和安全性之間的需求。

03.組合例項演示

3.1 需求分析

假設這樣的場景:

在電腦E盤有個資料夾,該資料夾下面有很多檔案,有影片檔案,有音訊檔案,有影像檔案,還有包含影片、音訊及影像的資料夾,十分雜亂,現希望將這些雜亂的檔案展示出來。更多內容

3.2 程式碼案例實現

不使用組合模式。注:當然可以一個迴圈遍歷就搞定了,因為這裡用的是檔案的形式,如果是別的形式呢?所以不要太較真了,只是舉例。

private void test() {
    MusicFile m1 = new MusicFile("盡頭.mp3");
    MusicFile m2 = new MusicFile("飄洋過海來看你.mp3");
    MusicFile m3 = new MusicFile("曾經的你.mp3");
    MusicFile m4 = new MusicFile("take me to your heart.mp3");

    VideoFile v1 = new VideoFile("戰狼2.mp4");
    VideoFile v2 = new VideoFile("理想.avi");
    VideoFile v3 = new VideoFile("琅琊榜.avi");

    ImageFile i1 = new ImageFile("敦煌.png");
    ImageFile i2 = new ImageFile("baby.jpg");
    ImageFile i3 = new ImageFile("girl.jpg");

    Folder aa = new Folder("aa");
    aa.addImage(i3);
    Folder bb = new Folder("bb");
    bb.addMusic(m4);
    bb.addVideo(v3);
    Folder top = new Folder("top");
    top.addFolder(aa);
    top.addFolder(bb);
    top.addMusic(m1);
    top.addMusic(m2);
    top.addMusic(m3);
    top.addVideo(v1);
    top.addVideo(v2);
    top.addImage(i1);
    top.addImage(i2);
    top.print();
}

public class MusicFile {
    private String name;

    public MusicFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class VideoFile {
    private String name;

    public VideoFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class ImageFile {
    private String name;

    public ImageFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class Folder {
    private String name;
    //音樂
    private List<MusicFile> musicList = new ArrayList<MusicFile>();
    //影片
    private List<VideoFile> videoList = new ArrayList<VideoFile>();
    //圖片
    private List<ImageFile> imageList = new ArrayList<ImageFile>();
    //資料夾
    private List<Folder> folderList = new ArrayList<Folder>();

    public Folder(String name){
        this.name = name;
    }

    public void addFolder(Folder folder){
        folderList.add(folder);
    }

    public void addImage(ImageFile image){
        imageList.add(image);
    }

    public void addVideo(VideoFile video){
        videoList.add(video);
    }

    public void addMusic(MusicFile music){
        musicList.add(music);
    }

    public void print(){
        for (MusicFile music : musicList){
            music.print();
        }
        for (VideoFile video : videoList){
            video.print();
        }
        for(ImageFile image : imageList){
            image.print();
        }
        for (Folder folder : folderList){
            folder.print();
        }
    }
}

3.3 是否可以最佳化

如果採用上述的形式,有幾個缺點:

  1. 資料夾類Folder的設計和實現都非常複雜,需要定義多個集合儲存不同型別的成員,而且需要針對不同的成員提供增加、刪除和獲取等管理和訪問成員的方法,存在大量的冗餘程式碼,系統維護較為困難;
  2. 由於系統沒有提供抽象層,客戶端程式碼必須有區別地對待充當容器的資料夾Folder和充當葉子的MusicFile、ImageFile和VideoFile,無法統一對它們進行處理;
  3. 系統的靈活性和可擴充套件性差,如果增加了新的型別的葉子和容器都需要對原有程式碼進行修改,例如如果需要在系統中增加一種新型別的文字檔案TextFile,則必須修改Folder類的原始碼,否則無法在資料夾中新增文字檔案。

3.4 組合設計

為了讓系統具有更好的靈活性和可擴充套件性,客戶端可以一致地對待檔案和資料夾,定義一個抽象構件AbstractFile,Folder充當容器構件,MusicFile、VideoFile和ImageFile充當葉子構件。更多內容

3.5 演變程式碼案例

抽象構件AbstractFile(Component)

public abstract class AbstractFile {
    public void add(AbstractFile file) {
        throw new UnsupportedOperationException();
    }

    public void remove(AbstractFile file) {
        throw new UnsupportedOperationException();
    }

    public AbstractFile getChild(int i) {
        throw new UnsupportedOperationException();
    }

    public void print() {
        throw new UnsupportedOperationException();
    }
}

葉子構件(Leaf)

public class MusicFile extends AbstractFile {
    private String name;

    public MusicFile(String name) {
        this.name = name;
    }

    public void print() {
        System.out.println(name);
    }
}

public class VideoFile extends AbstractFile {
    private String name;

    public VideoFile(String name) {
        this.name = name;
    }

    public void print() {
        System.out.println(name);
    }
}

public class ImageFile extends AbstractFile {
    private String name;

    public ImageFile(String name) {
        this.name = name;
    }

    public void print() {
        System.out.println(name);
    }
}

容器構件(Composite)

public class Folder extends AbstractFile {
    private String name;
    private List<AbstractFile> files = new ArrayList<AbstractFile>();

    public Folder(String name) {
        this.name = name;
    }

    @Override
    public void add(AbstractFile file) {
        files.add(file);
    }

    @Override
    public void remove(AbstractFile file) {
        files.remove(file);
    }

    @Override
    public AbstractFile getChild(int i) {
        return files.get(i);
    }

    @Override
    public void print() {
        for (AbstractFile file : files) {
            file.print();
        }
    }
}

客戶端測試:

private void test() {
    AbstractFile m1 = new MusicFile("盡頭.mp3");
    AbstractFile m2 = new MusicFile("飄洋過海來看你.mp3");
    AbstractFile m3 = new MusicFile("曾經的你.mp3");
    AbstractFile m4 = new MusicFile("take me to your heart.mp3");

    AbstractFile v1 = new VideoFile("戰狼2.mp4");
    AbstractFile v2 = new VideoFile("理想.avi");
    AbstractFile v3 = new VideoFile("琅琊榜.avi");

    AbstractFile i1 = new ImageFile("敦煌.png");
    AbstractFile i2 = new ImageFile("baby.jpg");
    AbstractFile i3 = new ImageFile("girl.jpg");

    AbstractFile aa = new Folder("aa");
    aa.add(i3);

    AbstractFile bb = new Folder("bb");
    bb.add(m4);
    bb.add(v3);

    AbstractFile top = new Folder("top");
    top.add(aa);
    top.add(bb);
    top.add(m1);
    top.add(m2);
    top.add(m3);
    top.add(v1);
    top.add(v2);
    top.add(i1);
    top.add(i2);

    top.print();
}

用組合模式提供一個抽象構件後,客戶端可以一致對待容器構件和葉子構件,進行統一處理,並且大量減少了冗餘,擴充套件性也很好,新增TextFile無需修改Folder原始碼,只需修改客戶端即可。

當然,這裡似乎有點違法“迭代器模式”中講的“單一職責原則”,的確是,抽象構件不但要管理層次結構,還要執行一些業務操作。

04.組合實現方式

4.1 組合模式分類

組合模式分為透明式的組合模式和安全式的組合模式。這兩種型別的主要區別在於抽象構件(Component)角色上的差別。

  1. 透明式的組合模式:在透明式的組合模式中,由於抽象構件宣告瞭所有子類中的全部方法,所以客戶端無須區別樹葉物件和樹枝物件,對客戶端來說是透明的。
  2. 安全式的組合模式:在安全式的組合模式中,將管理子構件的方法移到樹枝構件中,抽象構件只定義樹枝構件和樹葉構件所共同的方法。避免了透明式的組合模式的空實現或拋異常問題。

4.2 案例分析

案例:用組合模式實現在超市購物後,顯示並計算所選商品資訊與總價。更多內容

案例說明:張三在超市購物,購物清單如下

  • 1號小袋子裝了2 包芒果乾(單價15.8元),1包薯片(單價9.8元)
  • 2號小袋子裝了3 包山楂(單價7.8元),2包牛肉脯(單價19.8元)
  • 中型袋子裝了1號小袋子,1盒巧克力(單價39.8元)
  • 大型袋子裝了中型袋子,2號小袋子,1箱牛奶(單價79.8元)

大袋子的東西如下:

{
    1箱牛奶(單價79.8元)
    2號小袋子{
        3 包山楂(單價7.8元
        2包牛肉脯(單價19.8元)
    }
    中型袋子:{
        1盒巧克力(單價39.8元)
        1號小袋子:{
            2 包芒果乾(單價15.8元)
            1包薯片(單價9.8元)
        }
    }
}

4.3 透明式組合

透明式的組合模式中抽象構件還宣告訪問和管理子類的介面

元件(Component): 抽象構建角色。定義一個介面用於計算價格,顯示商品,並且可以新增子節點。

public interface Article {

    /**
     * 樹枝構件特有的方法: 訪問和管理子類的介面  大袋子裝小袋子
     */
    void add(Article article);

    /**
     * 計算價格
     */
    Double calculation();

    /**
     * 顯示商品
     */
    void show();
}

葉子節點(Leaf): 葉子節點物件,葉子節點沒有子節點。建立一個商品類,繼承抽象角色。

public class Goods implements Article {

    /**
     * 商品名稱
     */
    private String name;
    /**
     * 購買數量
     */
    private Integer quantity;
    /**
     * 商品單價
     */
    private Double unitPrice;

    public Goods(String name, Integer quantity, Double unitPrice) {
        this.name = name;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
    }

    /**
     * 樹枝構件特有的方法
     * 在樹葉構件中是能空實現或者拋異常
     */
    @Override
    public void add(Article article) {

    }

    @Override
    public Double calculation() {
        return this.unitPrice * this.quantity;
    }

    @Override
    public void show() {
        System.out.println(name + ": (數量:" + quantity + ",單價:" + unitPrice + "元)," +
                "合計:" + this.unitPrice * this.quantity + "元");
    }
}

複合節點(Composite): 建立袋子

/**
 * 樹枝構件: 袋子
 */
public class Bag implements Article {

    /**
     * 袋子名字
     */
    private String name;

    public Bag(String name) {
        this.name = name;
    }

    /**
     * 袋子中的商品
     */
    private List<Article> bags = new ArrayList<Article>();

    /**
     * 往袋子中新增袋子或者商品
     */
    @Override
    public void add(Article article) {
        bags.add(article);
    }

    @Override
    public Double calculation() {
        AtomicReference<Double> sum = new AtomicReference<>(0.0);
        bags.forEach(e -> {
            sum.updateAndGet(v -> v + e.calculation());
        });
        return sum.get();
    }

    @Override
    public void show() {
        bags.forEach(Article::show);
    }
}

客戶端(Client): 透過元件介面與組合結構進行互動。

private void test() {
    Article smallOneBag = new Bag("1號小袋子");
    smallOneBag.add(new Goods("芒果乾", 2, 15.8));
    smallOneBag.add(new Goods("薯片", 1, 9.8));

    Article smallTwoBag = new Bag("2號小袋子");
    smallTwoBag.add(new Goods("山楂", 3, 7.8));
    smallTwoBag.add(new Goods("牛肉脯", 2, 19.8));

    Article mediumBag = new Bag("中袋子");
    mediumBag.add(new Goods("巧克力", 1, 39.8));
    mediumBag.add(smallOneBag);

    Article BigBag = new Bag("大袋子");
    BigBag.add(new Goods("牛奶", 1, 79.8));
    BigBag.add(mediumBag);
    BigBag.add(smallTwoBag);

    System.out.println("打工充選購的商品有:");
    BigBag.show();
    Double sum = BigBag.calculation();
    System.out.println("要支付的總價是:" + sum + "元");
}

以上客戶端程式碼中 new Bag(),new Goods()的引用都是Article,無須區別樹葉物件和樹枝物件,對客戶端來說是透明的,此時Article呼叫add()是空實現或拋異常的(案例是空實現)。更多內容

4.4 安全式組合

安全式的組合模式中不宣告訪問和管理子類的介面,管理工作由樹枝構件完成,只定義一些通用的方法。

抽象構件(Component)角色。這裡建立定義一個介面用於計算價格,顯示商品。

public interface Article {
    /**
     * 計算價格
     */
    Double calculation();

    /**
     * 顯示商品
     */
    void show();
}

樹葉構件(Leaf)角色

public class Goods implements Article {

    /**
     * 商品名稱
     */
    private String name;
    /**
     * 購買數量
     */
    private Integer quantity;
    /**
     * 商品單價
     */
    private Double unitPrice;

    public Goods(String name, Integer quantity, Double unitPrice) {
        this.name = name;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
    }

    @Override
    public Double calculation() {
        return this.unitPrice * this.quantity;
    }

    @Override
    public void show() {
        System.out.println(name + ": (數量:" + quantity + ",單價:" + unitPrice + "元)," +
                "合計:" + this.unitPrice * this.quantity + "元");
    }
}

樹枝構件(Composite)角色 / 中間構件

/**
 * 樹枝構件: 袋子
 */
public class Bag implements Article {
    /**
     * 袋子名字
     */
    private String name;

    public Bag(String name) {
        this.name = name;
    }

    /**
     * 袋子中的商品
     */
    private List<Article> bags = new ArrayList<Article>();

    /**
     * 樹枝構件特有的方法: 訪問和管理子類的介面  大袋子裝小袋子
     * 往袋子中新增袋子或者商品
     */
    public void add(Article article) {
        bags.add(article);
    }

    @Override
    public Double calculation() {
        AtomicReference<Double> sum = new AtomicReference<>(0.0);
        bags.forEach(e -> {
            sum.updateAndGet(v -> v + e.calculation());
        });
        return sum.get();
    }

    @Override
    public void show() {
        bags.forEach(Article::show);
    }
}

客戶端(Client): 透過元件介面與組合結構進行互動。

private void test() {
    Bag smallOneBag = new Bag("1號小袋子");
    smallOneBag.add(new Goods("芒果乾", 2, 15.8));
    smallOneBag.add(new Goods("薯片", 1, 9.8));

    Bag smallTwoBag = new Bag("2號小袋子");
    smallTwoBag.add(new Goods("山楂", 3, 7.8));
    smallTwoBag.add(new Goods("牛肉脯", 2, 19.8));

    Bag mediumBag = new Bag("中袋子");
    mediumBag.add(new Goods("巧克力", 1, 39.8));
    mediumBag.add(smallOneBag);

    Bag BigBag = new Bag("大袋子");
    BigBag.add(new Goods("牛奶", 1, 79.8));
    BigBag.add(mediumBag);
    BigBag.add(smallTwoBag);

    System.out.println("打工充選購的商品有:");
    BigBag.show();
    Double sum = BigBag.calculation();
    System.out.println("要支付的總價是:" + sum + "元");
}

以上客戶端程式碼中 new Bag(),new Goods()的引用都是Bag,Goods,客戶端在呼叫時要知道樹葉物件和樹枝物件的存在。此時只有Bag才能呼叫add()。

05.組合模式分析

5.1 組合模式優點

  1. 使客戶端呼叫簡單,客戶端可以一致的使用組合結構或其中單個物件,使用者就不必關係自己處理的是單個物件還是整個組合結構,這就簡化了客戶端程式碼。
  2. 更容易在組合體內加入物件部件. 客戶端不必因為加入了新的物件部件而更改程式碼。更多內容

5.2 組合模式缺點

  1. 設計較複雜,需要明確類之間的層次關係;
  2. 不容易限制容器中的構件;
  3. 不容易用繼承的方法來增加構件的新功能;

5.3 適用環境

在以下情況下可以考慮使用組合模式:

  1. 在具有整體和部分的層次結構中,希望透過一種方式忽略整體與部分的差異,客戶端可以一致地對待它們。
  2. 在一個使用面嚮物件語言開發的系統中需要處理一個樹形結構。
  3. 在一個系統中能夠分離出葉子物件和容器物件,而且它們的型別不固定,需要增加一些新的型別。

5.4 模式擴充

在實際開發過程中,可以對樹葉節點和樹枝節點分別進行抽象,透過繼承的方式讓不同的樹葉節點和樹枝節點子類來實現行為。

5.5 使用建議說明

在設計時,優先使用介面而非具體類,以提高系統的靈活性和可維護性。更多內容

適用於需要處理複雜樹形結構的場景,如檔案系統、組織結構等。

06.外觀代理總結

6.1 總結一下學習

01.組合模式基礎

組合模式定義 :將物件以樹形結構組織起來,以達成“部分-整體”的層次結構,使得客戶端對單個物件和組合物件的使用具有一致性。

主要解決的問題 :1.簡化樹形結構中物件的處理,無論它們是單個物件還是組合物件。2.解耦客戶端程式碼與複雜元素的內部結構,使得客戶端可以統一處理所有型別的節點。

02.組合模式實現

組合模式包含如下角色:

  1. 元件(Component): 定義了組合中所有物件的通用介面,可以是抽象類或介面。它宣告瞭用於訪問和管理子元件的方法,包括新增、刪除、獲取子元件等。
  2. 葉子節點(Leaf): 表示組合中的葉子節點物件,葉子節點沒有子節點。它實現了元件介面的方法,但通常不包含子元件。
  3. 複合節點(Composite): 表示組合中的複合物件,複合節點可以包含子節點,可以是葉子節點,也可以是其他複合節點。它實現了元件介面的方法,包括管理子元件的方法。
  4. 客戶端(Client): 透過元件介面與組合結構進行互動,客戶端不需要區分葉子節點和複合節點,可以一致地對待整體和部分。

04.組合實現方式

組合模式分為透明式的組合模式和安全式的組合模式。這兩種型別的主要區別在於抽象構件(Component)角色上的差別。

  1. 透明式的組合模式:在透明式的組合模式中,由於抽象構件宣告瞭所有子類中的全部方法,所以客戶端無須區別樹葉物件和樹枝物件,對客戶端來說是透明的。
  2. 安全式的組合模式:在安全式的組合模式中,將管理子構件的方法移到樹枝構件中,抽象構件只定義樹枝構件和樹葉構件所共同的方法。避免了透明式的組合模式的空實現或拋異常問題。

6.2 更多內容推薦

模組描述備註
GitHub多個YC系列開源專案,包含Android元件庫,以及多個案例GitHub
部落格彙總匯聚Java,Android,C/C++,網路協議,演算法,程式設計總結等YCBlogs
設計模式六大設計原則,23種設計模式,設計模式案例,物件導向思想設計模式
Java進階資料設計和原理,物件導向核心思想,IO,異常,執行緒和併發,JVMJava高階
網路協議網路實際案例,網路原理和分層,Https,網路請求,故障排查網路協議
計算機原理計算機組成結構,框架,儲存器,CPU設計,記憶體設計,指令程式設計原理,異常處理機制,IO操作和原理計算機基礎
學習C程式設計C語言入門級別系統全面的學習教程,學習三到四個綜合案例C程式設計
C++程式設計C++語言入門級別系統全面的教學教程,併發程式設計,核心原理C++程式設計
演算法實踐專欄,陣列,連結串列,棧,佇列,樹,雜湊,遞迴,查詢,排序等Leetcode
Android基礎入門,開源庫解讀,效能最佳化,Framework,方案設計Android

23種設計模式

23種設計模式 & 描述 & 核心作用包括
建立型模式
提供建立物件用例。能夠將軟體模組中物件的建立和物件的使用分離
工廠模式(Factory Pattern)
抽象工廠模式(Abstract Factory Pattern)
單例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
結構型模式
關注類和物件的組合。描述如何將類或者物件結合在一起形成更大的結構
介面卡模式(Adapter Pattern)
橋接模式(Bridge Pattern)
過濾器模式(Filter、Criteria Pattern)
組合模式(Composite Pattern)
裝飾器模式(Decorator Pattern)
外觀模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
行為型模式
特別關注物件之間的通訊。主要解決的就是“類或物件之間的互動”問題
責任鏈模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
直譯器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
備忘錄模式(Memento Pattern)
觀察者模式(Observer Pattern)
狀態模式(State Pattern)
空物件模式(Null Object Pattern)
策略模式(Strategy Pattern)
模板模式(Template Pattern)
訪問者模式(Visitor Pattern)

6.3 更多內容

  • GitHub:https://github.com/yangchong211
  • 我的程式設計網站:https://yccoding.com
  • 部落格彙總:https://github.com/yangchong211/YCBlogs
  • 設計模式專欄:https://github.com/yangchong211/YCDesignBlog
  • Java高階進階專欄:https://github.com/yangchong211/YCJavaBlog
  • 網路協議專欄:https://github.com/yangchong211/YCNetwork
  • 計算機基礎原理專欄:https://github.com/yangchong211/YCComputerBlog

相關文章