Command
命令模式介紹:
Command 命令模式是一種對象行為型模式,它主要解決的問題是:在軟件構(gòu)建過程中,“行為請求者”與“行為實現(xiàn)者”通常呈現(xiàn)一種“緊耦合”的問題。如下圖:
?
有時我們必須向某對象提交請求,但并不知道關(guān)于被請求的操作或請求的接受者的任何信息,此時無法抵御變化的緊耦合是不合適的。如:需要對行為進行“記錄、撤銷 / 重做、事務(wù)”等處理。我們所要做的是將依賴關(guān)系轉(zhuǎn)化,將緊耦合變?yōu)樗神詈稀t上圖的形式轉(zhuǎn)化為如下形式:
??????
?
?????? Command 模式通過將請求本身變成一個對象來使行為請求者可向未指定的應(yīng)用對象提出請求。
?????? GoF 《設(shè)計模式》中說道:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數(shù)化;對請求排隊或記錄請求日志,以及支持可撤銷的操作。
Command
命令模式結(jié)構(gòu):
??????
?
定義場景:
?????? 現(xiàn)在來看一個場景:對于 notepad 大家都很熟悉,在我們使用 notepad 打開一個文檔之后,往往做一些操作,如;輸入字符 (Write) 、刪除前一個字符( Delete )、撤銷剛才的操作( UnDo )。現(xiàn)在我們就用 Console 程序模擬這個過程。
代碼實現(xiàn)與結(jié)構(gòu)分析:
?????? 在實現(xiàn)代碼前先說明實現(xiàn) Command 模式需要烤爐的一些問題:
1、? 一個命令對象應(yīng)達(dá)到何種智能程度:命令對象的能力可大可小。這樣就出現(xiàn)了兩個極端。一是:它僅確定一個接收者和執(zhí)行該請求的動作;一是:它自己實現(xiàn)所有功能,根本不需要額外的接收者對象。我給他們起了便于我方便記憶的名字,第一種叫 OperationCommand ,第二種叫 ObjectCommand 。當(dāng)然只是為了便于記憶和理解,如有不理解,在代碼實現(xiàn)與結(jié)構(gòu)分析最后我會再談?wù)勎业南敕ǎ缬胁煌祝€請多提意見,一會在下面的代碼中會分別對這兩種情況進行示范。
2、? 支持取消和重做:為了達(dá)到這個目的 ConcreteCommand 類中要存儲額外的狀態(tài)信息。也就是上圖中 ConcreteCommand 的 state 屬性。
3、? 避免取消操作中過程中的錯誤積累:由于命令重復(fù)的執(zhí)行、取消執(zhí)行和重執(zhí)行的過程可能會積累錯誤,以致一個應(yīng)用的狀態(tài)最終偏離初始值。這就有必要在 Command 中存入更多的信息以保證這些對象可被精確的復(fù)原。
下面來看看代碼上的實現(xiàn):首先,我先作一個 OperationCommand 的例子,先做一個請求的接收者 Document (也就是結(jié)構(gòu)圖中的 Receiver )
class
Document
??? {
???????
public
string strContent;
???????
public Document()
??????? {
??????????? strContent = "";
??????? }
}
在這個程序中我們還要定義一個抽象類Command,對于OperationCommand類型來說,它僅確定一個接收者和執(zhí)行該請求的動作。所以,在抽象類Command中,只聲明一個Excute的方法。這個方法在其子類中進行實現(xiàn)。(當(dāng)然這個Command還可以定義成接口)
abstract
class
Command
??? {
???????
public Command()
??????? { }
???????
public
abstract
void Excute();
}
接下來,就要實現(xiàn)各種操作(結(jié)構(gòu)圖中的ConcreteCommand),代碼如下
//
寫操作
class
WriteCommand : Command
??? {
???????
Document doc;
???????
ArrayList ObjectState;
???????
public WriteCommand(Document doc, ArrayList state)
??????? {
???????????
this.doc = doc;
??????????? ObjectState = state;
??????? }
???????
public
override
void Excute()
??????? {
??????????? doc.strContent += Console.ReadLine();
??????????? ObjectState.Add(doc.strContent);
??????? }
??? }
???
??? //
刪除操作
???
class
DeleteCommand : Command
??? {
???????
Document doc;
???????
ArrayList ObjectState;
???????
public DeleteCommand(Document doc, ArrayList state)
??????? {
???????????
this.doc = doc;
??????????? ObjectState = state;
??????? }
???????
public
override
void Excute()
??????? {
??????????? doc.strContent = doc.strContent.Substring(0, doc.strContent.Length - 1);
??????????? ObjectState.Add(doc.strContent);
??????? }
??? }
??? //
撤銷操作
???
class
UnDoCommand : Command
??? {
???????
Document doc;
???????
ArrayList ObjectState;
???????
public UnDoCommand(Document doc, ArrayList state)
??????? {
???????????
this.doc = doc;
??????????? ObjectState = state;
??????? }
???????
public
override
void Excute()
??????? {
??????????? doc.strContent = (string)ObjectState[ObjectState.Count - 2];
??????????? ObjectState.Add(doc.strContent);
??????? }
}
實現(xiàn)了各種操作后,編寫一個客戶代碼進行測試
class
Program
??? {
???????
static
void
??????? {
???????????
Document doc = newDocument();
???????????
Console.WriteLine("Please Input next operation:");
???????????
string strOperation = Console.ReadLine();
???
????????
Command com = null;
???????????
ArrayList ObjectState = newArrayList();//Record state
???????????
while (strOperation != "Exit")
??????????? {
???????????????
switch (strOperation.ToLower())
??????????????? {
???????????????????
case
"write":
???
????????????????????com = newWriteCommand(doc, ObjectState);
??????????????????????? com.Excute();
???????????????????????
Console.WriteLine("Write Operation:" + doc.strContent);
???????????????????????
break;
???????????????????
case
"
????????????
???????????com = newDeleteCommand(doc, ObjectState);
??????????????????????? com.Excute();
???????????????????????
Console.WriteLine("Delete Operation:" + doc.strContent);
???????????????????????
break;
???????????????????
case
"undo":
??????????????????
?????com = newUnDoCommand(doc, ObjectState);
??????????????????????? com.Excute();
???????????????????????
Console.WriteLine("UnDo Operation:" + doc.strContent);
???????????????????????
break;
???????????????????
default:
???????????????????????
Console.WriteLine("Wrong Operation:");
???????????????????????
break;
??????????????? }
???????????????
Console.WriteLine("Please Input next operation:");
??????????????? strOperation = Console.ReadLine();
??????????? }
??????? }
}
測試結(jié)果:
Please Input next operation:
write
k
Write Operation:k
Please Input next operation:
write
i
Write Operation:ki
Please Input next operation:
write
d
Write Operation:kid
Please Input next operation:
write
d
Write Operation:kidd
Please Input next operation:
Delete Operation:kid
Please Input next operation:
undo
UnDo Operation:kidd
Please Input next operation:
下面再來實現(xiàn)以下ObjectCommand的例子,首先還是編寫一個已存在的請求接收者Document(結(jié)構(gòu)圖中的Receiver)
class
Document
??? {
???????
public
string strContent;
???????
public Document()
??????? {
??????????? strContent = "";
??????? }
}
接下來實現(xiàn)抽象類Command(也可以使用接口),對于ObjectCommand類型來說,
它自己實現(xiàn)所有功能,根本不需要額外的接收者對象,所以在
Command
中聲明了所有的操作
abstract
class
Command
??? {
???????
public Command()
??????? {? }
???????
public
abstract
void Write();
???????
public
abstract
void Delete();
???????
public
abstract
void UnDo();
}
有了Command,就可以實現(xiàn)具體的操作類型DocumentCommand(結(jié)構(gòu)圖中的ConcreteCommand)
class
DocumentCommand : Command
??? {
???????
private
Document doc;
???????
private
ArrayList ObjectState = newArrayList();//Record State
???????
public DocumentCommand(Document doc)
??????? {
???????????
this.doc = doc;
??????? }
?
??????
public
override
void Write()
??????? {
???????????
Console.WriteLine("Please input an character:");
???????????
string strRead = Console.ReadLine();
??????????? doc.strContent += strRead;
??????????? ObjectState.Add(doc.strContent);
??????? }
????
???
public
override
void Delete()
??????? {
??????????? doc.strContent = doc.strContent.Substring(0, doc.strContent.Length - 1);
??????????? ObjectState.Add(doc.strContent);???????????
??????? }
???????
public
override
void UnDo()
??????? {
??????????? doc.strContent = (string)ObjectState[ObjectState.Count - 2];
??????????? ObjectState.Add(doc.strContent);
??????? }
}??
接下來就用一個客戶端代碼作一下測試
class
Program
??? {
???????
static
void
??????? {
???????????
Document doc = newDocument();
????
???????
DocumentCommand com = newDocumentCommand(doc);
???????????
Console.WriteLine("Please Input next operation:");
???????????
string strOperation = Console.ReadLine();
???????????
while (strOperation != "Exit")
??????????? {
???????????????
switch (strOperation.ToLower())
??????????????? {
???????????????????
case
"write":
??????????????????????? com.Write();
???????????????????????
Console.WriteLine("Write Operation:" + doc.strContent);
???????????????????????
break;
???????????????????
case
"
??
?????????????????????com.Delete();
???????????????????????
Console.WriteLine("Delete Operation:" + doc.strContent);
???????????????????????
break;
???????????????????
case
"undo":
??????????????????????? com.UnDo();
???????????????????????
Console.WriteLine("UnDo Operation:" + doc.strContent);
???????????????????????
break;
???????????????????
default:
???????????????????????
Console.WriteLine("Wrong Operation:");
???????????????????????
break;
??????????????? }
???????????????
Console.WriteLine("Please Input next operation:");
??????????????? strOperation = Console.ReadLine();
??????????? }
??????? }
??? }
測試結(jié)果如下:
Please Input next operation:
write
Please input an character:
k
Write Operation:k
Please Input next operation:
write
Please input an character:
i
Write Operation:ki
Please Input next operation:
write
Please input an character:
d
Write Operation:kid
Please Input next operation:
write
Please input an character:
d
Write Operation:kidd
Please Input next operation:
Delete Operation:kid
Please Input next operation:
undo
UnDo Operation:kidd
Please Input next operation:
這兩個程序中需要有幾點說明:
1、?????????????
對于OperationCommand,我的理解是它所實現(xiàn)的Command只是某一個操作對于某一個接收者,所以我給它取名為OperationCommand。對于ObjectCommand,是實現(xiàn)這樣一種對象,它實現(xiàn)了請求接收者的所有操作,所以取名為ObjectCommand
2、?????????????
在代碼實例中,我對狀態(tài)的保存處理相對簡單,但這是因為利用了String對象的特點,當(dāng)String對象被修改時,系統(tǒng)會重新分配一塊內(nèi)存。不修改原內(nèi)存上的內(nèi)容。如果是要保存其他的引用類型應(yīng)當(dāng)注意使用深拷貝,否則,所保存的狀態(tài)對象都指向一個內(nèi)存地址,隨著狀態(tài)的改變,保存不了原有的狀態(tài)。
3、?????????????
在對象狀態(tài)的保存上,我們可以使用Prototype模式。
Command
模式的幾個要點:
1、? Command 模式的根本目的在于將“行為請求者”與“行為實現(xiàn)者”解耦,在面向?qū)ο笳Z言中,常見的實現(xiàn)手段是“將行為抽象為對象”。
2、? 實現(xiàn) Command 接口的具體命令對象 ConcreteCommand 有時候根據(jù)需要可能會保存一些額外的狀態(tài)信息。
3、? 通過使用 Composite 模式,可以將多個“命名”封裝為一個“復(fù)合命令” MacroCommand 。
4、? Command 模式與 C# 中的 Delegate 有些類似。但兩者定義行為接口的規(guī)范有所區(qū)別: Command 以面向?qū)ο笾械摹敖涌?/span> - 實現(xiàn)”類定義行為接口規(guī)范,更嚴(yán)格,更符合抽象原則: Delegate 以函數(shù)簽名來定義行為接口規(guī)范,更靈活,但抽象能力比較弱