二十三種設計模式[14] - 命令模式(Command Pattern)
前言
命令模式,物件行為型模式的一種。它幫助我們將功能的呼叫者與實現者之間解耦(甚至完全解耦)。呼叫者與實現者之間並不是直接引用關係,呼叫者只需要知道如何傳送當前功能的請求即可,而不用關心該請求由誰在何時完成。
“ 將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。”
——《設計模式 - 可複用的面向物件軟體》
結構
- Command(命令類介面):宣告執行操作的介面;
- ConcreteCommand(命令類):Command介面的實現,用來呼叫具體的實現;
- Invoker(呼叫者):用來控制命令的執行,個人理解為Command的代理類;
- Receiver(接收者):功能具體的實現,由ConcreteCommand呼叫;
示例
考慮一個能夠控制各種智慧家電的App。在這個App中使用者可以隨意新增按鈕來控制某個家電的某個功能。也就是說當我們開發這個App時並不能確定使用者新增的按鈕是控制什麼家電執行什麼功能。現在我們使用命令模式來模擬
/// <summary> /// 命令的接收者 /// </summary> public class Television { public void Open() { Console.WriteLine("開啟電視機"); } public void Close() { Console.WriteLine("關閉電視機"); } } /// <summary> /// 命令介面 /// </summary> public interface ICommand { void Execute(); } /// <summary> /// 開機命令 /// </summary> public class TvOpenCommand : ICommand { private Television _tv = null; public TvOpenCommand(Television tv) { this._tv = tv; } public void Execute() { this._tv.Open(); } } /// <summary> /// 關機命令 /// </summary> public class TvCloseCommand : ICommand { private Television _tv = null; public TvCloseCommand(Television tv) { this._tv = tv; } public void Execute() { this._tv.Close(); } } /// <summary> /// 命令的呼叫者 /// </summary> public class Button { public ICommand Command { set; get; } public Button(ICommand command) { this.Command = command; } public void Click() { this.Command.Execute(); } } static void Main(string[] args) { Television tv = new Television(); //建立電視機物件(命令的接收者) ICommand tvOpen = new TvOpenCommand(tv); //建立開機命令 ICommand tvClose = new TvCloseCommand(tv); //建立關機命令 Button button = new Button(tvOpen); //建立開機按鈕(命令的呼叫者) button.Click(); //執行命令 button.Command = tvClose; //將按鈕功能變更為關機 button.Click(); //執行命令 Console.ReadKey(); }
在上述示例中,Button類充當呼叫者角色,Television類充當接收者角色。我們為Television類中的每一個函式都建立了命令類,不同的命令類決定了不同的操作,而該操作具體的實現由接收者完成。作為呼叫者的Button類並不知道它使用了哪個類執行了哪些操作,它只知道在它的Click函式中呼叫了ICommand介面的Execute函式。這就體現了命令模式的本質,將呼叫者與接收者解耦。
命令巨集
在日常生活中,我們往往希望通過一個按鈕來執行一系列操作(比如一鍵開啟電視和空調)。這個時候可以將命令模式與組合模式一同使用來實現一個命令巨集(又稱命令佇列)。
/// <summary> /// 命令的接收者 /// </summary> public class Television { public void Open() { Console.WriteLine("開啟電視機"); } public void Close() { Console.WriteLine("關閉電視機"); } } /// <summary> /// 命令接收者 /// </summary> public class AirConditioner { public void Open() { Console.WriteLine("開啟空調"); } public void Close() { Console.WriteLine("關閉空調"); } } /// <summary> /// 命令介面 /// </summary> public interface ICommand { void Execute(); } /// <summary> /// 開機命令 /// </summary> public class TvOpenCommand : ICommand { private Television _tv = null; public TvOpenCommand(Television tv) { this._tv = tv; } public void Execute() { this._tv.Open(); } } /// <summary> /// 關機命令 /// </summary> public class TvCloseCommand : ICommand { private Television _tv = null; public TvCloseCommand(Television t { this._tv = tv; } public void Execute() { this._tv.Close(); } } /// <summary> /// 開機命令 /// </summary> public class AcOpenCommand : ICommand { private AirConditioner _ac = null; public AcOpenCommand(AirConditioner ac) { this._ac = ac; } public void Execute() { this._ac.Open(); } } /// <summary> /// 關機命令 /// </summary> public class AcCloseCommand : ICommand { private AirConditioner _ac = null; public AcCloseCommand(AirConditioner ac) { this._ac = ac; } public void Execute() { this._ac.Close(); } } /// <summary> /// 命令巨集 /// </summary> public class MacorCommand : ICommand { public List<ICommand> Commands { set; get; } = new List<ICommand>(); public void Execute() { foreach (var command in this.Commands) { command.Execute(); } } } /// <summary> /// 命令的呼叫者 /// </summary> public class Button { public ICommand Command { set; get; } public Button(ICommand command) { this.Command = command; } public void Click() { this.Command.Execute(); } } static void Main(string[] args) { Television tv = new Television(); //電視物件(命令接收者) AirConditioner ac = new AirConditioner(); //空調物件(命令接收者) TvOpenCommand tvOpenCommand = new TvOpenCommand(tv); //電視開機命令 AcOpenCommand acOpenCommand = new AcOpenCommand(ac); //空調開機命令 MacorCommand macorCommand = new MacorCommand(); //巨集命令 macorCommand.Commands.Add(tvOpenCommand); //設定巨集命令 macorCommand.Commands.Add(acOpenCommand); //設定巨集命令 Button button = new Button(macorCommand); //建立巨集按鈕 button.Click(); //執行命令 Console.ReadKey(); }
示例中建立一個聚合了ICommand介面的MacorCommand類充當組合模式中的Compsite角色,用來儲存一系列的命令。它的存在能夠使呼叫者一次執行多個使用不同接收者的命令。
對於命令模式的擴充套件還有很多,比如請求日誌和逆向操作(撤銷)等等。這裡就不一一舉例了。
總結
命令模式的核心思想是將一個請求封裝,將一個請求命令的發出(呼叫)和接收處理分割開,達到將呼叫者與接收者解耦的目的。命令模式中的每個命令類都保持了一個很小的顆粒度,因為它只封裝了一個接收類中的一個函式。好處是在開發的過程中呼叫者不必關心也不知道具體執行的函式,保證了呼叫者與接收者的鬆耦合狀態,以便更好的控制和應對各種變化。同時也意味著令類需要封裝的函式越多命令類也就越多,存在類爆炸的風險。
以上,就是我對命令模式的理解,希望對你有所幫助。
示例原始碼:https://gitee.com/wxingChen/DesignPatternsPractice
系列彙總:https://www.cnblogs.com/wxingchen/p/9833744.html
本文著作權歸本人所有,如需轉載請標明本文連結(https://www.cnblogs.com/wxingchen/p/10031585.html)