iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0

前言

中秋快樂!!

定義


Composite is a structural design pattern that lets you compose objects into tree structures and then work with these structures as if they were individual objects.
--Refactoring Guru

Composite(組合模式)是一種結構型設計模式,它允許你將物件組合成樹狀結構,然後以結構的方式處理它們(一層一層的),就好像它們是個別的物件一樣。

可以把組合模式想像成一顆有葉子的樹!
客戶端可以對一片葉子作存取的動作,
或是對存取某個枝葉。

UML圖


我們先來看看它的UML圖~

組合模式使用時機


組合模式通常在以下情況下使用:

  • 需要表示 部分-整體 層次結構時:當你的應用程式中的對象具有部分-整體的層次結構,並且你想以一致的方式處理它們時,組合模式非常有用。

  • 希望客戶端統一處理個別物件和組合物件:組合模式允許客戶端對待個別物件和組合物件,而無需關心它們的具體類型,從而簡化了客戶端程式碼。

  • 需要構建具有層次結構的結構:當你需要構建具有內嵌(nested)結構的物件,例如樹狀結構、圖形界面元素,或者訂單和訂單項目等,組合模式能夠很好地支持這種需求。

  • 希望新增或刪除部分或整體內容而不影響客戶端程式:組合模式使你能夠添加新的部分或整體物件,同時不需要修改客戶端程式碼,這提高了系統的可擴展性。

  • 需要實現共享行為或運算:組合模式可以使你實現部分和整體物件之間的共享行為,從而避免程式碼的重複。

總之,組合模式適用於具有層次結構的物件,並且它能夠使客戶端程式碼更容易處理這些層次結構,從而提高程式碼的可維護性和可擴展性。

C# 實作範例~~


再用好吃的點心店舉例吧!
應景一點~
假設今天一家點心店賣了月餅和普洱茶~

首先先建立介面,展示架個還有品項

// 抽象點心類
abstract class Dessert
{
    protected string name;
    protected int priceNTD;

    public Dessert(string name, int priceNTD)
    {
        this.name = name;
        this.priceNTD = priceNTD;
    }

    public abstract int CalculateTotalPrice();

    public virtual void Display()
    {
        Console.WriteLine("單點心:" + name + ",價格:" + priceNTD.ToString("C0") + " (新台幣)");
    }
}

再來建立我們單一葉子物件!

// 葉子點心類 - 單一點心
class SingleDessert : Dessert
{
    public SingleDessert(string name, int priceNTD) : base(name, priceNTD) { }

    public override int CalculateTotalPrice()
    {
        return priceNTD;
    }
}

題外話
"C0" 是 .NET 中的一種數字格式化字符串。在這個上下文中,它表示格式化為不帶小數位的整數。當你將數字格式化為 "C0" 時,它將以通用的貨幣格式顯示,但不包括小數部分~

接著是我想要點心店有組合餐~
並且提供優惠價!

// 複合點心類 - 點心組合
class ComboDessert : Dessert
{
    private List<Dessert> desserts = new List<Dessert>();

    public ComboDessert(string name) : base(name, 0)
    {
        // 計算並設定合併的優惠價格
        CalculateDiscountedPrice();
    }

    public void AddDessert(Dessert dessert)
    {
        desserts.Add(dessert);
        // 在新增點心時,重新計算合併的優惠價格
        CalculateDiscountedPrice();
    }

    public void RemoveDessert(Dessert dessert)
    {
        desserts.Remove(dessert);
        // 在刪除點心時,重新計算合併的優惠價格
        CalculateDiscountedPrice();
    }

    private void CalculateDiscountedPrice()
    {
        // 計算合併的優惠價格,例如打折或組合優惠
        int totalRegularPrice = 0;
        foreach (var dessert in desserts)
        {
            totalRegularPrice += dessert.CalculateTotalPrice();
        }

        // 假設這裡有一個 10% 的組合優惠
        int discount = (int)(totalRegularPrice * 0.10);

        // 設定合併的優惠價格為總價減去折扣
        this.priceNTD = totalRegularPrice - discount;
    }

    public override int CalculateTotalPrice()
    {
        return priceNTD;
    }

    public override void Display()
    {
        Console.WriteLine("點心套餐:" + name);
        Console.WriteLine("包含以下點心:");
        foreach (var dessert in desserts)
        {
            dessert.Display();
        }

        Console.WriteLine("合併的優惠價格:" + priceNTD.ToString("C0") + " (新台幣)");
    }
}

最後
我們的客戶端程式碼~

class Program
{
    static void Main(string[] args)
    {
        // 創建點心
        var mooncake = new SingleDessert("月餅", 80);
        var puErTea = new SingleDessert("普洱茶", 40);

        // 創建點心套餐
        var dessertCombo = new ComboDessert("特別組合");
        dessertCombo.AddDessert(mooncake);
        dessertCombo.AddDessert(puErTea);

        // 顯示點心和點心套餐
        mooncake.Display();
        puErTea.Display();
        dessertCombo.Display();

        Console.ReadLine();
    }
}

output:


上一篇
[Day13] Design Pattern - Bridge橋接模式
下一篇
[Day15] Design Pattern - Decorator裝飾者模式
系列文
Design Pattern - 無所不在的設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言