之前大學時期曾經短暫的在飲料店打過工,想說體驗一下不同的工作,那時候也是我體重的巔峰,因為每天都可以喝飲料,只要在店裡,基本上所有的飲料都可以喝,而且想要加什麼料也隨便你,所以我常在我最愛喝的奶茶裡加入很多種不同的料,基本上一杯有一半以上是料了吧!(真的會肥死XD)
而今天要介紹的模式,正好很符合我在飲料店打工製作飲料的例子,假設我們要製作奶茶,那可能可以讓 MilkTea
這個類別去繼承Drink
這個抽象類別。如果今天是要做珍珠奶茶,以我們前面講了很多種的模式來分析,不太可能再做出一個BubbleMilkTea
去繼承自Drink
吧!畢竟已經有奶茶了,只是加珍珠進去就要多一個類別,這要所有組合都要實現就會製造出過多的類別出來。
我們可能會想到用 Bridge模式,讓增加一個介面,讓所有的配料都去實作這介面,並在抽象類別Drink
去引用,這樣我的每杯不同飲料現在都可以隨時抽換裡面的配料了!但是,假設今天我不只要加一種料呢?我想加兩種、加三種,這樣子 Bridge模式 就不太適用,此時我們就可以使用今天的主角,『Decorator』模式來幫助我們完成加配料的需求。
動態地給一個物件增加一些額外的職責。就增加功能來說,Decorator模式比生成子類別更為靈活。
(圖片來源:https://www.dofactory.com/img/diagrams/net/decorator.png)
Drink
,裡面會去定義到時候實例的名稱描述以及費用。IngredientDecorator
,他會繼承自Drink
,有一個建構式,到時會透過繼承自IngredientDecorator
的實體類別將繼承自Drink
的飲料帶入,IngredientDecorator
的繼承類(配料們)就能夠對飲料做加料的動作。IngredientDecorator
繼承自Drink
,主要是希望被加料完後的成品還是飲料Drink
,這樣我才能對他再做一次的加料動作,假設沒有繼承,那勢必就會需要做特殊化處理。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 ;
}
}
}
}
Decorator 模式幫助我們將問題分為以下兩部分:
如此能夠將 Decorator 物件的實作與決定如何使用Decorator的物件分離開來,而提高了內聚性,因為每個Decorator物件只用關心自己增加的功能,無須關心自己如何被增加到物件鏈中。這使我們能夠任意地重排 Decorator的順序,無須改變其的任何程式碼。
以下是使用Decorator 的時機: