命令模式的意圖一是將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進(jìn)行參數(shù)化;二是對請求排隊或記錄請求日志,以及支持可撤消的操作。簡略圖如下:
命令模式通過對命令的封裝,將命令的請求(調(diào)用者Invoker)和執(zhí)行(接收者Receiver)進(jìn)行了責(zé)任分離,委派給不同的對象,不僅使得調(diào)用者和執(zhí)行者之間實現(xiàn)了解耦(命令的請求方就不需要知道接收方的接口,也不需要知道命令是如何執(zhí)行的具體情況),還使得可以記錄命令的執(zhí)行記錄,添加執(zhí)行日志,使得命令的控制、執(zhí)行、取消和重做變得容易。
Delphi的Action就采用了這種模式,其中Windows控件(比如按鈕,菜單)是調(diào)用者,Action是命令,而接收者是Action的OnExecute事件的實現(xiàn)者。當(dāng)然,Delphi為了實現(xiàn)設(shè)計期間或者運(yùn)行期間控件的變灰,控件的Caption能在需要時與Action保持一致,在中間加了一個ActionLink類層次結(jié)構(gòu),采用的是橋模式,控件將控件與Action之間的通信交給ActionLink來完成,而AcionLink對象的創(chuàng)建則采用的是工廠方法(工廠就是控件本身),只不過它利用了GetActionLinkClass函數(shù)做得更加巧妙。而Action的具體執(zhí)行采用事件的方式,又使得命令和接收者進(jìn)一步解耦,這樣命令就不需要知道接收者的接口,也不用維護(hù)對接收者的引用,接收者也不需要知道命令的具體細(xì)節(jié),只需要提供一個符合要求的事件處理方法即可。
下面是示例代碼:
/// <summary>
/// Command類,定義一個執(zhí)行操作的接口,也可以是一個接口。
/// </summary>
public abstract class Command_Command
{
protected Command_Receiver _Receiver;
public abstract void Execute();
public Command_Receiver Receiver
{
get
{
return _Receiver;
}
set
{
_Receiver = value;
}
}
}
public class Command_ConcreateCommand : Command_Command
{
public Command_ConcreateCommand()
{
}
public override void Execute()
{
if(System.Windows.Forms.MessageBox.Show("你想執(zhí)行操作么?","系統(tǒng)提示!",
System.Windows.Forms.MessageBoxButtons.YesNo)==System.Windows.Forms.DialogResult.Yes)
{
if(this._Receiver!=null)
{
this._Receiver.Action();
}
}
}
}
public class Command_Receiver
{
public Command_Receiver()
{
}
public void Action()
{
System.Windows.Forms.MessageBox.Show("Command Execute!");
}
}
public class Command_Invoker
{
private Command_Command command;
public Command_Invoker()
{
}
public void SetCommand(Command_Command command)
{
this.command = command;
}
public void Execute()
{
this.command.Execute();
}
}
public class Command_Client
{
public static void Test()
{
//建立具體命令
Command_ConcreateCommand command = new Command_ConcreateCommand();
//建立具體接收者
Command_Receiver r1 = new Command_Receiver();
//鏈接命令與接收者
command.Receiver = r1;
//創(chuàng)建調(diào)用者
Command_Invoker invoker = new Command_Invoker();
//設(shè)置調(diào)用者的命令子類
invoker.SetCommand(command);
//執(zhí)行調(diào)用操作
invoker.Execute();
}
}
總結(jié):
如果沒有將命令的請求和執(zhí)行進(jìn)行責(zé)任分離并委托給不同的對象,那么請求與執(zhí)行都需要在同一個對象內(nèi)完成,比如Button的Click命令,但這就帶來一個問題,Button的Click邏輯太復(fù)雜,因為同樣是Button,不同的用戶請求Click命令時執(zhí)行的業(yè)務(wù)邏輯可能完全不同,而且通過Button的子類化來實現(xiàn)也根本不現(xiàn)實。所以,命令的請求和執(zhí)行的責(zé)任必須分離為好,要實現(xiàn)分離有兩種辦法,一是采用回調(diào)函數(shù)(原來的Windows系統(tǒng)比較普遍),二是采用事件或委托(Delphi對象方法),三就是采用命令模式。1,2實際上屬于同一類型的處理方式,好處是簡單(后記:這種應(yīng)用模式雖然比較普遍,但還有一個缺點就是頁面設(shè)計者和頁面邏輯實現(xiàn)很難分離,比如dotnet的aspx頁面和對應(yīng)的.cs之間的耦合由于太緊密,很難實現(xiàn)UI設(shè)計和UI邏輯實現(xiàn)的分離,silverlight的VM就是為了彌補(bǔ)這個缺陷.)。但缺點是沒法對命令本身進(jìn)行管理(執(zhí)行,取消,日志,重做等)。如果命令本身不需要管理和控制,使用2比較好,現(xiàn)在的界面控件的命令處理基本都是采用這種方式進(jìn)行。如果需要管理命令本身就要采用Command模式。
如果對接收者進(jìn)行抽象,就可以實現(xiàn)命令和接收者的動態(tài)組合,這有點類似裝飾模式,策略模式。
對命令模式的一個改進(jìn)就是可以將命令中接收者引用除掉,利用事件或者委托的辦法與接受者發(fā)生聯(lián)系。如果再對調(diào)用者進(jìn)行抽象,這樣就形成了一個調(diào)用者體系和命令體系,如果需要兩者的聯(lián)系可再增加一個聯(lián)系層,這樣就有3個相對獨(dú)立的層次體系,Delphi的Action模式就是這樣的。這樣做有利于系統(tǒng)的可視化設(shè)計。
后記:命令模式在現(xiàn)在的編程體系中,使用非常廣泛,特別是在UI層與控制層或者業(yè)務(wù)邏輯層之間的解耦合方面作用非常大,當(dāng)然,不好的地方是增加了系統(tǒng)的復(fù)雜度。silverlight編程中的mvvm模式中的vm層其實就可以看做是一個命令層,由這個層銜接M和V兩層,同時這個層與頁面層得銜接不再是傳統(tǒng)的頁面和.cs的關(guān)系,而是采用直接在UI中進(jìn)行綁定的方式進(jìn)行(傳統(tǒng)的方式是將頁面事件綁定到對應(yīng)的后臺.cs文件,這個文件在mvvm模式中也有,除了缺省的代碼和掛接VM的代碼外,已經(jīng)沒什么代碼,如果采用動態(tài)綁定V-VM,基本就只剩下缺省代碼了,因此UI設(shè)計完全不用考慮對這個文件的影響),這就使得頁面設(shè)計和頁面實現(xiàn)邏輯分離,可以很好的實現(xiàn)UI設(shè)計和UI邏輯開發(fā)的責(zé)任分割,從而實現(xiàn)設(shè)計更加專業(yè)化。副作用就是需要增加了很多類和接口。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
