.Net行為型設(shè)計模式之命令模式(Command)
一、動機(jī)(Motivate)
在我們的現(xiàn)實生活中有很多例子可以拿來說明這個模式,我們還拿吃餃子這個事情來說。我的奶奶說了,今天想吃餃子,發(fā)出了命令,然后我奶奶就去看電視去了。我們夫妻倆收到命令就開始和面,做餃子餡,包餃子。餃子包好了,我們就休息一會,等下午5點就開始燒水煮餃子了,晚飯的時間到了,我奶奶按時吃上了餃子。還有很多例子,就不一一列舉了。
在軟件構(gòu)建過程中,“行為請求者”與“行為實現(xiàn)者”通常呈現(xiàn)一種“緊耦合”。但在某些場合——比如需要對行為進(jìn)行“記錄、撤銷/重做(undo/redo)、事務(wù)”等處理,這種無法抵御變化的緊耦合是不合適的。在這種情況下,如何將“行為請求者”與“行為實現(xiàn)者”解耦?將一組行為抽象為對象,可以實現(xiàn)二者之間的松耦合。
二、意圖(Intent)
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶(客戶程序,也是行為的請求者)進(jìn)行參數(shù)化;對請求排隊或記錄請求日志,以及支持可撤銷的操作。 ——《設(shè)計模式》GoF
三、結(jié)構(gòu)圖(Structure)
四、模式的組成
從命令模式的結(jié)構(gòu)圖可以看出,它涉及到五個角色,它們分別是:
(1)、客戶角色(Client):創(chuàng)建具體的命令對象,并且設(shè)置命令對象的接收者。注意這個不是我們常規(guī)意義上的客戶端,而是在組裝命令對象和接收者,或許,把這個Client稱為裝配者會更好理解,因為真正使用命令的客戶端是從Invoker來觸發(fā)執(zhí)行。
(2)、命令角色(Command):聲明了一個給所有具體命令類實現(xiàn)的抽象接口。
(3)、具體命令角色(ConcreteCommand):命令接口實現(xiàn)對象,是“虛”的實現(xiàn);通常會持有接收者,并調(diào)用接收者的功能來完成命令要執(zhí)行的操作。
(4)、請求者角色(Invoker):要求命令對象執(zhí)行請求,通常會持有命令對象,可以持有很多的命令對象。這個是客戶端真正觸發(fā)命令并要求命令執(zhí)行相應(yīng)操作的地方,也就是說相當(dāng)于使用命令對象的入口。
(5)、接受者角色(Receiver):接收者,真正執(zhí)行命令的對象。任何類都可能成為一個接收者,只要它能夠?qū)崿F(xiàn)命令要求實現(xiàn)的相應(yīng)功能。
五、命令模式的代碼實現(xiàn)
下面以生活中吃餃子為例來說說如何實現(xiàn)命令模式吧。今天早上,我奶奶就發(fā)布了命令,說她老人家想吃豬肉大蔥餡的餃子。我奶奶腿腳不好,就讓我爸爸捎個話給我們夫妻倆,晚上要吃豬肉大蔥餡的餃子。我瞬間就明白了,這個偉大的任務(wù)就落到我們夫妻倆肩上了。說做就做,保證晚飯能吃上熱氣騰騰的餃子,具體實現(xiàn)代碼如下:
static void Main(string[] args) { //奶奶想吃豬肉大蔥餡的餃子 PatrickLiuAndWife liuAndLai = new PatrickLiuAndWife();//命令接受者 Command command = new MakeDumplingsCommand(liuAndLai);//命令 PaPaInvoker papa = new PaPaInvoker(command); //命令請求者 //奶奶發(fā)布命令 papa.ExecuteCommand(); //奶奶說不想吃餃子了 papa.Undo(); } //這個類型就是請求者角色--也就是我爸爸的角色,告訴奶奶要吃餃子 public sealed class PaPaInvoker { //我爸爸從奶奶那里接受到的命令 private Command _command; //爸爸開始接受具體的命令 public PaPaInvoker(Command command) { this._command = command; } //爸爸給我們下達(dá)命令 public void ExecuteCommand() { _command.MakeDumplings(); } public void Undo() { _command.UndoMakeDumplings(); } } //該類型就是抽象命令角色--Commmand,定義了命令的抽象接口,任務(wù)是包餃子 public abstract class Command { //真正任務(wù)的接受者 protected PatrickLiuAndWife _worker; protected Command(PatrickLiuAndWife worker) { _worker = worker; } //該方法就是抽象命令對象Command的Execute方法 public abstract void MakeDumplings(); public abstract void UndoMakeDumplings(); } //該類型是具體命令角色--ConcreteCommand,這個命令完成制作“豬肉大蔥餡”的餃子 public sealed class MakeDumplingsCommand : Command { public MakeDumplingsCommand(PatrickLiuAndWife worker) : base(worker) { } //執(zhí)行命令--包餃子 public override void MakeDumplings() { //執(zhí)行命令---包餃子 _worker.Execute("今天包的是農(nóng)家豬肉和農(nóng)家大蔥餡的餃子"); } public override void UndoMakeDumplings() { //執(zhí)行命令---撤銷包餃子 _worker.Execute("撤銷包餃子"); } } //該類型是具體命令接受角色Receiver,具體包餃子的行為是我們夫妻倆來完成的 public sealed class PatrickLiuAndWife { //這個方法相當(dāng)于Receiver類型的Action方法 public void Execute(string job) { Console.WriteLine(job); } }
六、命令模式的實現(xiàn)要點:
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ū)ο笾械?ldquo;接口-實現(xiàn)”來定義行為接口規(guī)范,更嚴(yán)格,更符合抽象原則;Delegate以函數(shù)簽名來定義行為接口規(guī)范,更靈活,但抽象能力比較弱。
5、使用命令模式會導(dǎo)致某些系統(tǒng)有過多的具體命令類。某些系統(tǒng)可能需要幾十個,幾百個甚至幾千個具體命令類,這會使命令模式在這樣的系統(tǒng)里變得不實際。
1、命令模式的優(yōu)點:
(1)、命令模式使得新的命令很容易被加入到系統(tǒng)里。
(2)、可以設(shè)計一個命令隊列來實現(xiàn)對請求的Undo和Redo操作。
(3)、可以較容易地將命令寫入日志。
(4)、可以把命令對象聚合在一起,合成為合成命令。合成命令式合成模式的應(yīng)用。
2、命令模式的缺點:
使用命令模式可能會導(dǎo)致系統(tǒng)有過多的具體命令類。這會使得命令模式在這樣的系統(tǒng)里變得不實際。
3、命令模式的使用場景:
(1)、系統(tǒng)需要支持命令的撤銷(undo)。命令對象可以把狀態(tài)存儲起來,等到客戶端需要撤銷命令所產(chǎn)生的效果時,可以調(diào)用undo方法把命令所產(chǎn)生的效果撤銷掉。命令對象還可以提供redo方法,以供客戶端在需要時,再重新實現(xiàn)命令效果。
(2)、系統(tǒng)需要在不同的時間指定請求、將請求排隊。一個命令對象和原先的請求發(fā)出者可以有不同的生命周期。意思為:原來請求的發(fā)出者可能已經(jīng)不存在了,而命令對象本身可能仍是活動的。這時命令的接受者可以在本地,也可以在網(wǎng)絡(luò)的另一個地址。命令對象可以串行地傳送到接受者上去。
(3)、如果一個系統(tǒng)要將系統(tǒng)中所有的數(shù)據(jù)消息更新到日志里,以便在系統(tǒng)崩潰時,可以根據(jù)日志里讀回所有數(shù)據(jù)的更新命令,重新調(diào)用方法來一條一條地執(zhí)行這些命令,從而恢復(fù)系統(tǒng)在崩潰前所做的數(shù)據(jù)更新。
(4)、系統(tǒng)需要使用命令模式作為“CallBack(回調(diào))”在面向?qū)ο笙到y(tǒng)中的替代。Callback即是先將一個方法注冊上,然后再以后調(diào)用該方法。
七、.NET 中命令模式的實現(xiàn)
由于.NET有了Delegate,它很少很少用到Command。它只要需要用到行為抽象,它都用Delegate去做。因為這是Framework,這是和業(yè)務(wù)領(lǐng)域相關(guān)度不大的基礎(chǔ)建設(shè)層面,它是不太需要用到OO的層面。對于我們來說,我們建議更多地用Command去實現(xiàn)。
到此這篇關(guān)于.Net行為型設(shè)計模式之命令模式(Command)的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- .Net行為型設(shè)計模式之備忘錄模式(Memento)
- .Net行為型設(shè)計模式之訪問者模式(Visitor)
- .Net行為型設(shè)計模式之職責(zé)鏈模式(Chain of Responsibility)
- .Net行為型設(shè)計模式之策略模式(Stragety)
- .Net行為型設(shè)計模式之狀態(tài)模式(State)
- .Net行為型設(shè)計模式之中介者模式(Mediator)
- .Net行為型設(shè)計模式之觀察者模式(Observer)
- .Net行為型設(shè)計模式之迭代器模式(Iterator)
- .Net行為型設(shè)計模式之模板方法模式(Template?Method)
- .Net行為型設(shè)計模式之解釋器模式(Interpreter)
相關(guān)文章
.Net行為型設(shè)計模式之模板方法模式(Template?Method)
這篇文章介紹了.Net行為型設(shè)計模式之模板方法模式(Template?Method),文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05ASP.NET在VS2022中使用Dispose釋放資源實例
這篇文章介紹了ASP.NET在VS2022中使用Dispose釋放資源實例,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11發(fā)布WEB站點時出現(xiàn)Server Application Unavailable
發(fā)布WEB站點時出現(xiàn)Server Application Unavailable...2006-12-12.Net創(chuàng)建型設(shè)計模式之原型模式(Prototype)
這篇文章介紹了.Net設(shè)計模式之原型模式(Prototype),文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05