# 15. Command ###### tags: `DesignPatterns` ## 關於 Command 本篇將討論以下幾個問題 > ### 1. 關於 Command > ### 2. UML > ### 3. 將 UML 轉為程式碼 > ### 4. 情境 --- ## 測試環境: >OS:Windows 10 >IDE:Visual Studio 2019 --- ## 1. 關於 Command > Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. > > by [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) - 將請求封裝為物件,可藉由不同的請求(e.g. Queue、Log requests),對呼叫端請求參數化,並支援取消操作 Command(命令)屬於行為型(Behavioral Patterns),當遇到想將**不同工作參數化傳入並加入觸發邏輯**時,可使用 Command 來將工作與觸發邏輯切割開來,調用端不需要知道實作細節即可呼叫。 優點: - 符合 單一職責原則(Single Responsibility Principle) - 符合 開閉原則(Open Closed Principle) 缺點: - 由於職責拆分較細,class 數量增加會造成整體程式複雜度提升 --- ## 2. UML ![](https://i.imgur.com/UNGayC4.jpg) Class 間關聯: - Client 依賴 ConcreteCommand - Client & ConcreteCommand 關聯 Receiver - ConcreteCommand 繼承 Command - Invoker 可包含 Command Class: - Client:呼叫端 - Receiver:被呼叫端 - Command:命令的抽象類別或介面 - ConcreteCommand:命令實作 - Invoker:儲存與調用命令 --- ## 3. 將 UML 轉為程式碼 命令介面 ```C# /// <summary> /// 命令介面 /// </summary> public interface ICommand { public void Execute(); } ``` 命令實作 ```C# /// <summary> /// 命令實作 /// </summary> public class ConcreteCommand : ICommand { public Receiver _receiver { get; } public ConcreteCommand(Receiver receiver) { _receiver = receiver; } public void Execute() { _receiver.Action(); } } ``` 被呼叫端 ```C# /// <summary> /// 被呼叫端 /// </summary> public class Receiver { public void Action() { Console.WriteLine("Called Receiver.Action()"); } } ``` 儲存與調用命令 ```C# /// <summary> /// 儲存與調用命令 /// </summary> public class Invoker { private ICommand _command { get; set; } public void SetCommand(ICommand command) { _command = command; } public void RemoveCommand() { _command = null; } public void ExecuteCommand() { _command.Execute(); } } ``` 1. 於`command`中加載`receiver` 2. `invoker`中加載`command`並執行 ```C# static void Main(string[] args) { Default.Receiver receiver = new Default.Receiver(); Default.ICommand command = new Default.ConcreteCommand(receiver); Default.Invoker invoker = new Default.Invoker(); invoker.SetCommand(command); invoker.ExecuteCommand(); Console.ReadLine(); } ``` 執行結果 ```Console Called Receiver.Action() ``` --- ## 4. 情境 我們接到了一個線上下訂咖啡、餐盒的需求 - 咖啡是由飲料部區的員工負責 - 餐盒是由熟食部的負責 - 顧客都是從同一介面下訂,由系統自動通知對應部門 點餐介面 ```C# /// <summary> /// 點餐介面 /// </summary> public interface IOrder { public void Execute(); } ``` 點咖啡實作 & 點餐盒實作 ```C# /// <summary> /// 點咖啡實作 /// </summary> public class OrderCoffee : IOrder { public Employee Employee { get; } public OrderCoffee(Employee employee) { Employee = employee; } public void Execute() { Employee.Coffee(); } } /// <summary> /// 點餐盒實作 /// </summary> public class OrderBoxedMeal : IOrder { public Employee Employee { get; } public OrderBoxedMeal(Employee employee) { Employee = employee; } public void Execute() { Employee.BoxedMeal(); } } ``` 員工,包含熟食部 & 飲料部 ```C# /// <summary> /// 員工,包含熟食部 & 飲料部 /// </summary> public class Employee { // 咖啡 public void Coffee() { Console.WriteLine("飲料部收到 咖啡 訂單"); } // 餐盒 public void BoxedMeal() { Console.WriteLine("熟食部收到 餐盒 訂單"); } } ``` 儲存與調用命令 ```C# /// <summary> /// 儲存與調用命令 /// </summary> public class Invoker { private IOrder _Order { get; set; } public void SetOrder(IOrder order) { _Order = order; } public void RemoveOrder() { _Order = null; } public void ExecuteCommand() { _Order.Execute(); } } ``` 1. `employee`加載到點咖啡`orderCoffee` & 點餐盒`orderBoxedMeal` 2. `invoker`加載點咖啡 & 點餐盒並執行點餐 ```C# static void Main(string[] args) { Situation.Employee employee = new Situation.Employee(); Situation.IOrder orderCoffee = new Situation.OrderCoffee(employee); Situation.IOrder orderBoxedMeal = new Situation.OrderBoxedMeal(employee); Situation.Invoker invoker = new Situation.Invoker(); invoker.SetOrder(orderCoffee); invoker.ExecuteCommand(); invoker.SetOrder(orderBoxedMeal); invoker.ExecuteCommand(); Console.ReadLine(); } ``` 執行結果 ```Console 飲料部收到 咖啡 訂單 熟食部收到 餐盒 訂單 ``` --- ## 完整程式碼 GitHub:[Behavioral_02_Command](https://github.com/darionnnnnn/blog/tree/master/Blog/Behavioral_02_Command) --- ## 總結 ### 在 [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) 的說明中還提到了 Queue、Log requests 還有取消操作,關於這幾個部分屬於複雜情況的應用,故本篇並未提及,可以在了解本篇的 Command 基本架構與概念之後,再去找找相關內容來閱讀,會上手得更加快速。 --- ## 參考資料 1. [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) 2. [大話設計模式](https://www.tenlong.com.tw/products/9789866761799) 2. [dofactory](https://www.dofactory.com/) 3. [Refactoring.Guru](https://refactoring.guru/) --- ## 新手上路,若有錯誤還請告知,謝謝