一、定義
將一個請求封裝為一個物件,從而可以用不同的請求對客戶進行引數化,對請求排隊或者記錄請求日誌,以及支援可撤銷的操作。命令模式是一種物件行為型模式,其別名為動作(Action)模式或事物(Transaction)模式。
二、描述
命令模式的本質是對請求進行封裝,一個請求對應一個命令,將發出命令的責任和執行命令的責任分割開,使得請求的一方不必瞭解接收請求的一方的介面,更不必知道請求如何被接收、操作是否被執行、何時被執行,以及是怎麼被執行的。包含以下四個角色:
1、Command(抽象命令類):抽象命令類一般是一個抽象類或介面,在其中宣告瞭用於執行請求的Execute()方法,透過這些方法可以呼叫請求接收者的相關操作。
2、ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類,實現了抽象命令類中宣告的方法,它對應具體的接收者物件,將接收者物件的動作繫結其中。具體命令類在實現Execute()方法時,將呼叫接收者物件的相關操作(Action)。
3、Invoker(呼叫者):呼叫者即請求傳送者,它透過命令物件來執行請求。一個呼叫者不需要在設計時確定其接收者,因此只與抽象命令之間存在關聯關係。在程式執行時可以將一個具體命令物件注入其中,再呼叫具體命令物件的Execute()方法,從而實現簡介呼叫請求接收者的相關命令。
4、Receiver(接收者):接收者執行與請求相關的操作,具體實現對請求的業務處理。
三、例子
X公司開發人員開發了一個桌面版應用程式,該應用程式為員工提供了一系列自定義功能鍵,透過這些功能鍵來實現一些快捷操作,例如:“開啟幫助文件”、“最小化至托盤”、“自動截圖”等。
FBSettingWindow:“功能鍵設定”介面類,充當客戶端
public class FBSettingWindow
{
// 視窗標題
public string Title { get; set; }
// 所有功能鍵集合
private IList<FunctionButton> functionButtonList = new List<FunctionButton>();
public FBSettingWindow(string title)
{
this.Title = title;
}
public void AddFunctionButton(FunctionButton fb)
{
functionButtonList.Add(fb);
}
public void RemoveFunctionButton(FunctionButton fb)
{
functionButtonList.Remove(fb);
}
// 顯示視窗及功能鍵
public void Display()
{
Console.WriteLine("顯示視窗:{0}", this.Title);
Console.WriteLine("顯示功能鍵:");
foreach (var fb in functionButtonList)
{
Console.WriteLine(fb.Name);
}
Console.WriteLine("------------------------------------------");
}
}
FunctionButton:請求傳送類,充當呼叫者
public class FunctionButton
{
// 功能鍵名稱
public string Name { get; set; }
// 維持一個抽象命令物件的引用
private Command command;
public FunctionButton(string name)
{
this.Name = name;
}
// 為功能鍵注入命令
public void SetCommand(Command command)
{
this.command = command;
}
// 傳送請求的方法
public void OnClick()
{
Console.WriteLine("點選功能鍵:");
if (command != null)
{
command.Execute();
}
}
}
Command:抽象命令類
public abstract class Command
{
public abstract void Execute();
}
HelpCommand、MinimizeCommand:幫助類、最小化類,充當具體命令類
public class HelpCommand : Command
{
private HelpHandler hander;
public HelpCommand()
{
hander = new HelpHandler();
}
// 命令執行方法,將呼叫請求接受者的業務方法
public override void Execute()
{
if (hander != null)
{
hander.Display();
}
}
}
public class MinimizeCommand : Command
{
private WindowHandler handler;
public MinimizeCommand()
{
handler = new WindowHandler();
}
// 命令執行方法,將呼叫請求接受者的業務方法
public override void Execute()
{
if (handler != null)
{
handler.Minimize();
}
}
}
WindowHandler、HelpHandler:最小化處理類、幫助處理類,充當接收者
public class WindowHandler
{
public void Minimize()
{
Console.WriteLine("正在最小化視窗至托盤...");
}
}
public class HelpHandler
{
public void Display()
{
Console.WriteLine("正在顯示幫助文件...");
}
}
Program:客戶端測試類
//Step1.模擬顯示功能鍵設定視窗
FBSettingWindow window = new FBSettingWindow("功能鍵設定視窗");
// Step2.假如目前要設定兩個功能鍵
FunctionButton buttonA = new FunctionButton("功能鍵A");
FunctionButton buttonB = new FunctionButton("功能鍵B");
// Step3.讀取配置檔案和反射生成具體命令物件
Command commandA = new HelpCommand();
Command commandB = new MinimizeCommand();
// Step4.將命令注入功能鍵
buttonA.SetCommand(commandA);
buttonB.SetCommand(commandB);
window.AddFunctionButton(buttonA);
window.AddFunctionButton(buttonB);
window.Display();
// Step5.呼叫功能鍵的業務方法
buttonA.OnClick();
buttonB.OnClick();
Console.ReadLine("");
四、總結
1、優點
(1)降低了系統的耦合度,了系統的耦合度。由於請求者與接收者之間不存在直接引用,因此請求者與接收者之間實現了完全解耦,相同的請求者可以對應不同的接收者,同樣,相同的接收者也可以供不同的請求者使用,兩者之間具有良好的獨立性。
(2)透過使用命令模式,新的命令可以很容易地加入到系統中。由於增加新的具體命令類不會影響其他類,所以增加新的具體命令類很容易,無須修改原有系統原始碼,甚至客戶類程式碼,滿足開閉原則的要求。
(3)使用命令模式可以比較容易地設計一個命令佇列或宏命令(組合命令)。
(4)命令模式為請求的撤銷(Undo)和恢復(Redo)操作提供了一種設計和實現方案。
2、缺點
(1)使用命令模式可能會導致某些系統有過多的具體命令類,因為針對每一個對請求接收者的呼叫操作都需要設計一個具體命令,所以在某些系統中可能需要提供大量的具體命令類,這將影響命令模式的使用。