關於 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
- 將請求封裝為物件,可藉由不同的請求(e.g. Queue、Log requests),對呼叫端請求參數化,並支援取消操作
Command(命令)屬於行為型(Behavioral Patterns),當遇到想將不同工作參數化傳入並加入觸發邏輯時,可使用 Command 來將工作與觸發邏輯切割開來,調用端不需要知道實作細節即可呼叫。
優點:
- 符合 單一職責原則(Single Responsibility Principle)
- 符合 開閉原則(Open Closed Principle)
缺點:
- 由於職責拆分較細,class 數量增加會造成整體程式複雜度提升
2. UML
Class 間關聯:
- Client 依賴 ConcreteCommand
- Client & ConcreteCommand 關聯 Receiver
- ConcreteCommand 繼承 Command
- Invoker 可包含 Command
Class:
- Client:呼叫端
- Receiver:被呼叫端
- Command:命令的抽象類別或介面
- ConcreteCommand:命令實作
- Invoker:儲存與調用命令
3. 將 UML 轉為程式碼
命令介面
/// <summary>
/// 命令介面
/// </summary>
public interface ICommand
{
public void Execute();
}
命令實作
/// <summary>
/// 命令實作
/// </summary>
public class ConcreteCommand : ICommand
{
public Receiver _receiver { get; }
public ConcreteCommand(Receiver receiver)
{
_receiver = receiver;
}
public void Execute()
{
_receiver.Action();
}
}
被呼叫端
/// <summary>
/// 被呼叫端
/// </summary>
public class Receiver
{
public void Action()
{
Console.WriteLine("Called Receiver.Action()");
}
}
儲存與調用命令
/// <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();
}
}
- 於
command
中加載receiver
invoker
中加載command
並執行
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();
}
執行結果
Called Receiver.Action()
4. 情境
我們接到了一個線上下訂咖啡、餐盒的需求
- 咖啡是由飲料部區的員工負責
- 餐盒是由熟食部的負責
- 顧客都是從同一介面下訂,由系統自動通知對應部門
點餐介面
/// <summary>
/// 點餐介面
/// </summary>
public interface IOrder
{
public void Execute();
}
點咖啡實作 & 點餐盒實作
/// <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();
}
}
員工,包含熟食部 & 飲料部
/// <summary>
/// 員工,包含熟食部 & 飲料部
/// </summary>
public class Employee
{
// 咖啡
public void Coffee()
{
Console.WriteLine("飲料部收到 咖啡 訂單");
}
// 餐盒
public void BoxedMeal()
{
Console.WriteLine("熟食部收到 餐盒 訂單");
}
}
儲存與調用命令
/// <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();
}
}
employee
加載到點咖啡orderCoffee
& 點餐盒orderBoxedMeal
invoker
加載點咖啡 & 點餐盒並執行點餐
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();
}
執行結果
飲料部收到 咖啡 訂單
熟食部收到 餐盒 訂單
完整程式碼
GitHub:Behavioral_02_Command