iT邦幫忙

2022 iThome 鐵人賽

DAY 14
1

之前大學時期曾經短暫的在飲料店打過工,想說體驗一下不同的工作,那時候也是我體重的巔峰,因為每天都可以喝飲料,只要在店裡,基本上所有的飲料都可以喝,而且想要加什麼料也隨便你,所以我常在我最愛喝的奶茶裡加入很多種不同的料,基本上一杯有一半以上是料了吧!(真的會肥死XD)

而今天要介紹的模式,正好很符合我在飲料店打工製作飲料的例子,假設我們要製作奶茶,那可能可以讓 MilkTea這個類別去繼承Drink這個抽象類別。如果今天是要做珍珠奶茶,以我們前面講了很多種的模式來分析,不太可能再做出一個BubbleMilkTea去繼承自Drink吧!畢竟已經有奶茶了,只是加珍珠進去就要多一個類別,這要所有組合都要實現就會製造出過多的類別出來。

我們可能會想到用 Bridge模式,讓增加一個介面,讓所有的配料都去實作這介面,並在抽象類別Drink去引用,這樣我的每杯不同飲料現在都可以隨時抽換裡面的配料了!但是,假設今天我不只要加一種料呢?我想加兩種、加三種,這樣子 Bridge模式 就不太適用,此時我們就可以使用今天的主角,『Decorator』模式來幫助我們完成加配料的需求。

Decorator - 定義

動態地給一個物件增加一些額外的職責。就增加功能來說,Decorator模式比生成子類別更為靈活。

https://ithelp.ithome.com.tw/upload/images/20220922/20136443fNnlUY13pW.png

(圖片來源:https://www.dofactory.com/img/diagrams/net/decorator.png)

範例 UML

https://ithelp.ithome.com.tw/upload/images/20220925/20136443hvI2f6SVie.png

Code要點

  • 有一個抽象類別Drink,裡面會去定義到時候實例的名稱描述以及費用。
  • 配料的裝飾器,我們用成抽象類別IngredientDecorator,他會繼承自Drink,有一個建構式,到時會透過繼承自IngredientDecorator的實體類別將繼承自Drink的飲料帶入,IngredientDecorator的繼承類(配料們)就能夠對飲料做加料的動作。
  • 會讓IngredientDecorator繼承自Drink,主要是希望被加料完後的成品還是飲料Drink,這樣我才能對他再做一次的加料動作,假設沒有繼承,那勢必就會需要做特殊化處理。

不囉嗦上Code!

using System;

namespace DAY14_Decorator
{
    class Program
    {
        static void Main(string[] args)
        {
            // 點了一杯奶茶, 我要加珍珠
            Drink milkTea = new MilkTea();
            // 將奶茶帶入珍珠的建構子
            milkTea = new Pearl(milkTea);
            Console.WriteLine($"{milkTea.GetDescription()}; 花費:${milkTea.Cost()}");

            // 再點一杯綠茶,我要加珍珠和椰果
            Drink greenTea = new GreenTea();
            greenTea = new Pearl(greenTea);
            greenTea = new CoconutJelly(greenTea);
            Console.WriteLine($"{greenTea.GetDescription()}; 花費:${greenTea.Cost()}");

            Console.ReadLine();
        }
        public abstract class Drink
        {
            public string Description { get; set; }
            public virtual string GetDescription()
            {
                return Description;
            }
            // 留給子類別實作
            public abstract decimal Cost();
        }

        public abstract class IngredientDecorator : Drink
        {
            protected Drink _drink;
            public IngredientDecorator(Drink drink)
            {
                _drink = drink;
            }
        }

        // 奶茶
        public class MilkTea : Drink
        {
            public MilkTea()
            {
                Description = "奶茶";
            }
            public override decimal Cost()
            {
                return 50;
            }
        }

        // 綠茶
        public class GreenTea : Drink
        {
            public GreenTea()
            {
                Description = "綠茶";
            }
            public override decimal Cost()
            {
                return 30;
            }
        }

        // 椰果
        public class CoconutJelly : IngredientDecorator
        {
            public CoconutJelly(Drink drink) : base(drink)
            {
            }

            public override string GetDescription()
            {
                return _drink.GetDescription() + " + 椰果";
            }

            public override decimal Cost()
            {
                return _drink.Cost() + 15;
            }
        }

        // 珍珠
        public class Pearl : IngredientDecorator
        {
            public Pearl(Drink drink) : base(drink)
            {
            }

            public override string GetDescription()
            {
                return _drink.GetDescription() + " + 珍珠";
            }

            public override decimal Cost()
            {
                return _drink.Cost() + 10 ;
            }
        }
    }
}
  • 結果

https://ithelp.ithome.com.tw/upload/images/20220922/20136443JzA2DbrknA.png

簡單的小結

Decorator 模式幫助我們將問題分為以下兩部分:

  • 如何實作提供新功能的物件。
  • 如何為每種特殊情況組織物件。

如此能夠將 Decorator 物件的實作與決定如何使用Decorator的物件分離開來,而提高了內聚性,因為每個Decorator物件只用關心自己增加的功能,無須關心自己如何被增加到物件鏈中。這使我們能夠任意地重排 Decorator的順序,無須改變其的任何程式碼。

以下是使用Decorator 的時機:

  • 存在幾種可選的功能。
  • 這些裝飾物件可能遵循也可能不遵循所有規則。
  • 需要某種方式以所需的不同順序呼叫這些裝飾物件,但是又不能加重客戶物件的負擔。
  • 不希望應用程式必須承擔知道使用哪些裝飾物件(甚或是否存在)的職責。

上一篇
【DAY13】淺談設計模式的原則與策略
下一篇
【DAY15】Template模式 - 那些年,我們一起搖的飲料(下)
系列文
勇闖秘境!探索物件導向背後的設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言