Design Pattern - Decorator裝飾者模式

Decorator裝飾者模式:

裝飾者模式通常用來動態的添加物件的前後新功能或行為,不需要時也方便移除該功能,而不需要修改原始類別的程式碼,允許你將功能封裝於獨立的類別中,組合這些裝飾者而實現功能。

透過裝飾者模式也符合開放封閉原則,對擴充是開放的,對修改是封閉的。

 

用裝飾者點飲料

點一杯飲料可能需要加許多料,可能有很多種不同的情境及價錢的計算。

假設一杯"基本飲料款"(被裝飾者),需要加入草莓、巧克力、半糖,可以用裝飾者模式實現,那假如今天有另一杯飲料的款式,也可以通過加入草莓、巧克力、半糖裝飾者加入,而不用修改現有的程式。

internal class Program
{
   static void Main(string[] args)
   {
       //基本飲料
       BasicDrink basicDrink = new BasicDrink();
       Console.WriteLine(basicDrink.AddCost() + "元");
       Console.WriteLine(basicDrink.AddMaterial());
       
       //加入草莓
       IDrink strawberryDrink = new StrawberryDecorator(basicDrink);
       Console.WriteLine(strawberryDrink.AddCost() + "元");
       Console.WriteLine(strawberryDrink.AddMaterial());
       
       //加入巧克力
       IDrink chocolateDrink = new ChocolateDecorator(strawberryDrink);
       Console.WriteLine(chocolateDrink.AddCost() + "元");
       Console.WriteLine(chocolateDrink.AddMaterial());
       
       //加入半糖
       IDrink halfSugarDrink = new HalfSugarDecorator(chocolateDrink);
       Console.WriteLine(halfSugarDrink.AddCost() + "元");
       Console.WriteLine(halfSugarDrink.AddMaterial());
       Console.ReadLine();
   }
}

/// <summary>
/// 飲料介面
/// </summary>
public interface IDrink
{
   string AddMaterial(); //加入飲料材料
   double AddCost();  //加入金額
}

/// <summary>
/// 基本飲料類別(可定義不同飲料基底)
/// </summary>
public class BasicDrink : IDrink
{
   public double AddCost()
   {
       return 10;
   }
   public string AddMaterial()
   {
       return "基本飲料款";
   }
}

/// <summary>
/// 飲料裝飾者
/// </summary>
public abstract class DrinkDecorator : IDrink
{
   protected IDrink drink; //被裝飾的實體
   public DrinkDecorator(IDrink drink)
   { 
       this.drink = drink; 
   }
   public virtual double AddCost()
   {
       return drink.AddCost();
   }
   public virtual string AddMaterial()
   {
       return drink.AddMaterial();
   }
}

/// <summary>
/// 草莓裝飾者
/// </summary>
public class StrawberryDecorator : DrinkDecorator
{
   public StrawberryDecorator(IDrink drink) : base(drink)
   {
   }
   public override double AddCost() 
   {
       return base.AddCost() + 15;
   }
   public override string AddMaterial()
   {
       return base.AddMaterial() + " + 草莓";
   }
}

/// <summary>
/// 巧克力裝飾者
/// </summary>
public class ChocolateDecorator : DrinkDecorator
{
   public ChocolateDecorator(IDrink drink) : base(drink)
   {
   }
   public override double AddCost()
   {
       return base.AddCost() + 20;
   }
   public override string AddMaterial()
   {
       return base.AddMaterial() + " + 巧克力";
   }
}

/// <summary>
/// 半糖裝飾者
/// </summary>
public class HalfSugarDecorator : DrinkDecorator
{
   public HalfSugarDecorator(IDrink drink) : base(drink)
   {
   }
   public override double AddCost()
   {
       return base.AddCost(); //糖不用加錢,也可以不寫此方法override
   }
   public override string AddMaterial()
   {
       return base.AddMaterial() + " + 半糖";
   }
}

裝飾者也很適合用來執行有順序的行為

例如吃檔後需對一個Table的操作,行為模式可能是:

刪除所有資料在全部新增,又或者只刪除吃檔沒有存在於Table資料,在新增及更新重複的資料


  internal class Program
 {
     static void Main(string[] args)
     {
         #region 全部刪除,在新增資料
         IDBAction txtFile = new TxtFileProcess(); //讀取Txt檔案程序
         IDBAction truncateTable = new TruncateTable(txtFile);  //刪除所有資料
         IDBAction insertTable = new InsertTable(truncateTable); //新增資料
         IDBAction countTableRecords = new CountTableRecords(insertTable); //計算筆數
         #endregion

         #region 刪除吃檔不存在的資料,在新增或更新重複資料
         //IDBAction txtFile = new TxtFileProcess(); //讀取Txt檔案程序
         //IDBAction deleteTable = new DeleteTable(txtFile);  //刪除吃檔不存在的資料
         //IDBAction insertOrUpdateTable = new InsertOrUpdateTable(deleteTable); //新增或更新重複資料
         //IDBAction countTableRecords = new CountTableRecords(insertOrUpdateTable); //計算筆數                 
         #endregion

         countTableRecords.Execute(); //執行

         Console.ReadLine();
     }
 }

 /// <summary>
 /// DB執行介面
 /// </summary>
 public interface IDBAction
 {
     void Execute();
 }

 /// <summary>
 /// DB裝飾者
 /// </summary>
 public abstract class DBActionDecorator : IDBAction
 {
     protected IDBAction action;
     public DBActionDecorator(IDBAction dBAction)
     {
         this.action = dBAction;
     }
     public virtual void Execute()
     {
         if (this.action != null)
             this.action.Execute();
     }
 }

 /// <summary>
 /// Txt讀檔處理程序
 /// </summary>
 public class TxtFileProcess : IDBAction
 {
     public void Execute()
     {
         Console.WriteLine("讀取檔案");
     }
 }

 /// <summary>
 /// 刪除所有資料
 /// </summary>
 public class TruncateTable : DBActionDecorator
 {
     public TruncateTable(IDBAction dBAction) : base(dBAction)
     {
     }
     public override void Execute()
     {
         base.Execute();
         Console.WriteLine("刪除所有資料");
     }
 }

 /// <summary>
 /// 刪除吃檔不存在的資料
 /// </summary>
 public class DeleteTable : DBActionDecorator
 {
     public DeleteTable(IDBAction dBAction) : base(dBAction)
     {
     }
     public override void Execute()
     {
         base.Execute();
         Console.WriteLine("刪除吃檔沒有資料");
     }
 }

 /// <summary>
 /// 新增資料
 /// </summary>
 public class InsertTable : DBActionDecorator
 {
     public InsertTable(IDBAction dBAction) : base(dBAction)
     {
     }
     public override void Execute()
     {
         base.Execute();
         Console.WriteLine("新增資料");
     }
 }

 /// <summary>
 /// 新增或更新重複資料
 /// </summary>
 public class InsertOrUpdateTable : DBActionDecorator
 {
     public InsertOrUpdateTable(IDBAction dBAction) : base(dBAction)
     {
     }
     public override void Execute()
     {
         base.Execute();
         Console.WriteLine("新增或更新重複的資料");
     }
 }

 /// <summary>
 /// 計算筆數
 /// </summary>
 public class CountTableRecords : DBActionDecorator
 {
     public CountTableRecords(IDBAction dBAction) : base(dBAction)
     {
     }
     public override void Execute()
     {
         base.Execute();
         Console.WriteLine("計算筆數");
     }
 }

全部刪除,在新增資料

刪除吃檔不存在的資料,在新增或更新重複資料