目的:
任何模式的出現(xiàn),都是為了解決一些特定的場景的耦合問題,以達到對修改封閉,對擴展開放的效果。命令模式也不例外:
命令模式是為了解決命令的請求者和命令的實現(xiàn)者之間的耦合關(guān)系。
解決了這種耦合的好處我認為主要有兩點:
1.更方便的對命令進行擴展(注意:這不是主要的優(yōu)勢,后面會提到)
2.對多個命令的統(tǒng)一控制(這種控制包括但不限于:隊列、撤銷/恢復(fù)、記錄日志等等)
模式解析:
經(jīng)典的命令模式包括4個角色:
Command:定義命令的統(tǒng)一接口
ConcreteCommand:Command接口的實現(xiàn)者,用來執(zhí)行具體的命令,某些情況下可以直接用來充當Receiver。
Receiver:命令的實際執(zhí)行者
Invoker:命令的請求者,是命令模式中最重要的角色。這個角色用來對各個命令進行控制。
下面對上面四個角色的經(jīng)典實現(xiàn)用代碼來進行說明,這也是大部分文章對命令模式的運用方式。
////// Command角色
///public interface ICommand? ? {? ? ? ? void Execute();? ? }? ? ////// ConcreteCommand角色A
///public class ConcreteCommandA : ICommand? ? {? ? ? ? private Receiver receiver = null;? ? ? ? public ConcreteCommandA(Receiver receiver)? ? ? ? {? ? ? ? ? ? this.receiver = receiver;? ? ? ? }? ? ? ? public void Execute()? ? ? ? {? ? ? ? ? ? this.receiver.DoA();? ? ? ? }? ? }? ? ////// ConcreteCommand角色B
///public class ConcreteCommandB : ICommand? ? {? ? ? ? private Receiver receiver = null;? ? ? ? public ConcreteCommandB(Receiver receiver)? ? ? ? {? ? ? ? ? ? this.receiver = receiver;? ? ? ? }? ? ? ? public void Execute()? ? ? ? {? ? ? ? ? ? this.receiver.DoB();? ? ? ? }? ? }? ? ////// Receiver角色
///public class Receiver? ? {? ? ? ? public void DoA()? ? ? ? {? ? ? ? ? ? //DoSomething? ? ? ? }? ? ? ? public void DoB()? ? ? ? {? ? ? ? ? ? //DoSomething? ? ? ? }? ? }? ? ////// Invoker角色
///public class Invoker? ? {? ? ? ? private ICommand command = null;? ? ? ? //設(shè)置命令? ? ? ? public void SetCommand(ICommand command)? ? ? ? {? ? ? ? ? ? this.command = command;? ? ? ? }? ? ? ? //執(zhí)行命令? ? ? ? public void RunCommand()? ? ? ? {? ? ? ? ? ? command.Execute();? ? ? ? }? ? }? ? ////// 客戶端調(diào)用
///public class Client? ? {? ? ? ? public Client()? ? ? ? {? ? ? ? ? ? Receiver receiver = new Receiver();? ? ? ? ? ? Invoker invoker = new Invoker();? ? ? ? ? ? invoker.SetCommand(new ConcreteCommandA(receiver));? ? ? ? ? ? invoker.RunCommand();? ? ? ? ? ? invoker.SetCommand(new ConcreteCommandB(receiver));? ? ? ? ? ? invoker.RunCommand();? ? ? ? }? ? }
1. 確實可以兩個類來搞定。但我們要牢記命令模式的初衷:對命令請求者(Invoker)和命令實現(xiàn)者(Receiver)的解耦,方便對命令進行各種控制。
打個比方:現(xiàn)在我們要對ConcreteCommandA與ConcreteCommandB以及其他一系列命令進行日志記錄,并且兩個命令之間的操作間隔不能大于1秒。
這種情況下要直接用兩個類就會有大量的業(yè)務(wù)邏輯要在客戶端進行處理,當命令增加,對每個命令的控制增加時,就會在Client里面產(chǎn)生大量的變化點,這樣耦合就出來了,但是采用命令模式之后,對著一系列的命令我們都可以進行控制,這就是對變化點的封裝,實際Invoker代碼如下:
public class Invoker
{
private ICommand lastCommand = null;
private DateTime lastDateTime = DateTime.Now;
public void RunCommand(ICommand command)
{
//記錄操作日志
Console.WriteLine(command.GetType().Name);
//大于1秒,執(zhí)行命令
if (lastCommand == null || (DateTime.Now - this.lastDateTime).TotalSeconds > 1)
{
lastCommand = command;
lastDateTime = DateTime.Now;
command.Execute();
}
//小于1秒時不執(zhí)行,并進行相應(yīng)處理
Console.WriteLine("操作間隔過短!");
}
}
2. 增加命令:采用命令模式的時候,我感覺最大的耦合點變化到了Receiver和ConcreteCommand之間,當然我們可以對Receiver進行抽象,采用接口或者抽象類來封裝這個變化,但實際情況中我們會遇到多個命令來至于不同的Receiver,比如A,B兩個命令來至于ReceiverAB,C命令來至于ReceiverC,這種情況下我們怎么應(yīng)對命令的新增?對這種情況我的理解是命令模式并不能也不需要解決這個問題,因為命令模式的操作單元已經(jīng)細化到了每一個具體的功能上面,當增加一個具體功能的時候是沒有很好的辦法對功能實現(xiàn)類進行修改關(guān)閉的(當然你可以把每個功能方法放到一個類中,但確實沒必要,這個粒度已經(jīng)很小了),實際上也沒有必要的。
打個比方:一個界面有增加刪除功能,在一個類Receiver里面實現(xiàn)了,現(xiàn)在要新增一個修改功能,有必要新增一個Reeciver類嗎?我自己的答案是沒有必要。
但是當業(yè)務(wù)是所有的功能都會同時修改時,我們就可以對這Receiver進行抽象,提取出IReceiver。
適用場景:
1.?命令的發(fā)送者和命令執(zhí)行者有不同的生命周期。命令發(fā)送了并不是立即執(zhí)行。
2. 命令需要進行各種管理邏輯。
3.?需要支持撤消\重做操作(這種狀況的代碼大家可以上網(wǎng)搜索下,有很多,這里不進行詳細解讀)。
結(jié)論:
通過對上面的分析我們可以知道如下幾點:
1. 命令模式是通過命令發(fā)送者和命令執(zhí)行者的解耦來完成對命令的具體控制的。
2.?命令模式是對功能方法的抽象,并不是對對象的抽象。
3.?命令模式是將功能提升到對象來操作,以便對多個功能進行一系列的處理以及封裝。