Composite 組合模式簡介與 C# 範例【結構型3】【設計模式來了_8】

2023-10-10 15:00:18

〇、簡介

1、什麼是組合設計模式?

一句話解釋:

  針對樹形結構的任意節點,都實現了同一介面,他們具有相同的操作,可以通過某一操作來遍歷全部節點。

組合模式通過使用樹形結構來組合物件,用來表示部分以及整體層次。組合模式屬於結構型模式,多用於遞迴。

官方意圖描述:將物件組合成樹形結構,以表示「部分-整體」的層次結構。Composite 使得使用者對單個物件和組合的使用具有一致性。

一個比喻:(學校裡年級、班級架構)

  無論是年級主任,還是各班主任或任課老師,以及每個班級的學生,都屬於學校中的一員,但是他們又屬於分級管理,比如班主任管理一個班級。

2、優缺點和適用場景

  • 高層模組呼叫簡單。組合模式通過提供統一的介面來隱藏物件的層次結構,使高層模組只需要關心物件的行為,而不需要關心物件的結構。這樣可以使高層模組的程式碼更加簡潔和易於理解。
  • 節點自由,不需要關心物件層次。組合模式通過使用樹形結構來組合物件,可以動態地增加或刪除物件,而不需要修改高層模組的程式碼。這樣可以使系統更加靈活和可延伸。
  • 簡化了使用者端程式碼,因為不論物件多麼複雜使用者端都是以同一套介面操作。
  • 葉子節點(Leaf)會繼承得到一些它所不需要(管理子類操作的方法)的方法,這與設計模式介面隔離原則相違背。組合模式中的葉子節點(Leaf)也需要實現 Component 介面,而這個介面中可能包含了一些它所不需要的方法。這會使得葉子節點的程式碼變得冗餘和複雜,不符合介面隔離原則。
  • 組合類的參照開銷可能會較大。組合模式中的組合類需要包含一個 Component 陣列,這個陣列的大小是固定的,如果組合類的範例需要包含大量的元件物件,那麼組合類的參照開銷可能會較大。
  • 如果需要確定某個元件是特殊組織,然後針對它做特殊的操作,就需要在執行時判斷。

適用場景:

  • 希望使用者端可以忽略組合物件與單個物件的差異時。組合模式通過提供統一的介面來隱藏物件的層次結構,使使用者端可以忽略組合物件與單個物件的差異。
  • 物件層次具備整體和部分,呈樹形結構。組合模式通過使用樹形結構來組合物件,可以用來表示物件層次具備整體和部分,呈樹形結構的情況。例如:樹形選單,檔案、資料夾的管理。

一、簡單的程式碼範例

下邊是一個簡單的範例,模擬樹形結構,包含多層次:

class Program
{
    static void Main(string[] args)
    {
        Composite root = new Composite("根節點");
        Composite composite1 = new Composite("--組合節點1");
        Composite composite2 = new Composite("--組合節點2");
        Leaf leaf1 = new Leaf("----葉子節點1");
        Leaf leaf2 = new Leaf("----葉子節點2");
        composite1.Add(leaf1);
        composite1.Add(leaf2);
        composite2.Add(leaf1);
        root.Add(composite1);
        root.Add(composite2);
        root.Operation();
    }
}
// 抽象元件
public abstract class Component
{
    public string Name { get; set; }
    public Component(string name)
    {
        Name = name;
    }
    public virtual void Operation()
    {
        Console.WriteLine("執行操作");
    }
}
// 葉子元件
public class Leaf : Component
{
    public Leaf(string name) : base(name) { }
    public override void Operation()
    {
        Console.WriteLine($"{Name} 是葉子節點,不包含子節點");
    }
}
// 容器元件
public class Composite : Component
{
    private List<Component> _children = new List<Component>();
    public Composite(string name) : base(name) { }
    public void Add(Component component)
    {
        _children.Add(component);
    }
    public void Remove(Component component)
    {
        _children.Remove(component);
    }
    public override void Operation()
    {
        Console.WriteLine($"{Name} 是容器節點,包含子節點");
        foreach (var child in _children)
        {
            child.Operation();
        }
    }
}

二、根據範例程式碼看結構

Component:為組合中的物件宣告介面,在適當的情況下,實現所有類共有介面的預設行為。它可為一個介面,用於存取和管理 Component 的子元件。另外,可在遞迴結構中定義一個介面,用於存取一個父部件,並在合適的情況下實現它。

Leaf:在組合中表示葉節點物件,葉結點沒有子節點。另外,也可以定義圖元物件的行為。(圖元物件是計算機圖學中的一個概念,是指由點、線、面等基本幾何元素組成的三維模型的基本單元。圖元物件通常用於描述三維模型的幾何形狀和空間位置等資訊。)

Composite:定義有子部件的那些部件的行為,可通過 Components 儲存子部件,以及在 Component 介面中實現與子部件有關的操作。

Client:通過 Component 介面操縱組合部件的物件。

三、相關模式

通常,部件-父部件連線用於 Chain of Responsibility 責任鏈模式。

Decorator 裝飾模式經常和 Composite 模式一起使用。當裝飾和組合一起使用時,它們通常由一個公共的父類別。因此裝飾必須具有 Add、Remove 和 GetChild 操作的 Component 介面。

Flyweight 享元模式允許共用元件,但不能參照其父部件。

Itertor 迭代器模式可用於遍歷 Composite。

Visitor 存取者模式將本來應該分佈在 Composite 和 Leaf 類中的操作和行為區域性化。