一、前言之前一直在忙于工作上的事情,關(guān)于設(shè)計(jì)模式系列一直沒(méi)更新,最近項(xiàng)目中發(fā)現(xiàn),對(duì)于設(shè)計(jì)模式的了解是必不可少的,當(dāng)然對(duì)于設(shè)計(jì)模式的應(yīng)用那更是重要,可以說(shuō)是否懂得應(yīng)用設(shè)計(jì)模式在項(xiàng)目中是衡量一個(gè)程序員的技術(shù)水平,因?yàn)閷?duì)于一個(gè)功能的實(shí)現(xiàn),高級(jí)工程師和初級(jí)工程師一樣都會(huì)實(shí)現(xiàn),但是區(qū)別在于它們實(shí)現(xiàn)功能的可擴(kuò)展和可維護(hù)性,也就是代碼的是否“優(yōu)美”、可讀。但是,要更好地應(yīng)用,首先就必須了解各種設(shè)計(jì)模式和其應(yīng)用場(chǎng)景,所以我還是希望繼續(xù)完成設(shè)計(jì)模式這個(gè)系列,希望通過(guò)這種總結(jié)的方式來(lái)加深自己設(shè)計(jì)模式的理解。 二、命令模式的介紹2.1 命令模式的定義命令模式屬于對(duì)象的行為型模式。命令模式是把一個(gè)操作或者行為抽象為一個(gè)對(duì)象中,通過(guò)對(duì)命令的抽象化來(lái)使得發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分隔開(kāi)。命令模式的實(shí)現(xiàn)可以提供命令的撤銷和恢復(fù)功能。 2.2 命令模式的結(jié)構(gòu)既然,命令模式是實(shí)現(xiàn)把發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開(kāi),然而中間必須有某個(gè)對(duì)象來(lái)幫助發(fā)出命令者來(lái)傳達(dá)命令,使得執(zhí)行命令的接收者可以收到命令并執(zhí)行命令。例如,開(kāi)學(xué)了,院領(lǐng)導(dǎo)說(shuō)計(jì)算機(jī)學(xué)院要進(jìn)行軍訓(xùn),計(jì)算機(jī)學(xué)院的學(xué)生要跑1000米,院領(lǐng)導(dǎo)的話也就相當(dāng)于一個(gè)命令,他不可能直接傳達(dá)給到學(xué)生,他必須讓教官來(lái)發(fā)出命令,并監(jiān)督學(xué)生執(zhí)行該命令。在這個(gè)場(chǎng)景中,發(fā)出命令的責(zé)任是屬于學(xué)院領(lǐng)導(dǎo),院領(lǐng)導(dǎo)充當(dāng)與命令發(fā)出者的角色,執(zhí)行命令的責(zé)任是屬于學(xué)生,學(xué)生充當(dāng)于命令接收者的角色,而教官就充當(dāng)于命令的發(fā)出者或命令請(qǐng)求者的角色,然而命令模式的精髓就在于把每個(gè)命令抽象為對(duì)象。從而命令模式的結(jié)構(gòu)如下圖所示: 從命令模式的結(jié)構(gòu)圖可以看出,它涉及到五個(gè)角色,它們分別是:
2.3 命令模式的實(shí)現(xiàn)現(xiàn)在,讓我們以上面的軍訓(xùn)的例子來(lái)實(shí)現(xiàn)一個(gè)命令模式,在實(shí)現(xiàn)之前,可以參考下命令模式的結(jié)構(gòu)圖來(lái)分析下實(shí)現(xiàn)過(guò)程。 軍訓(xùn)場(chǎng)景中,具體的命令即是學(xué)生跑1000米,這里學(xué)生是命令的接收者,教官是命令的請(qǐng)求者,院領(lǐng)導(dǎo)是命令的發(fā)出者,即客戶端角色。要實(shí)現(xiàn)命令模式,則必須需要一個(gè)抽象命令角色來(lái)聲明約定,這里以抽象類來(lái)來(lái)表示。命令的傳達(dá)流程是: 命令的發(fā)出者必須知道具體的命令、接受者和傳達(dá)命令的請(qǐng)求者,對(duì)應(yīng)于程序也就是在客戶端角色中需要實(shí)例化三個(gè)角色的實(shí)例對(duì)象了。 命令的請(qǐng)求者負(fù)責(zé)調(diào)用命令對(duì)象的方法來(lái)保證命令的執(zhí)行,對(duì)應(yīng)于程序也就是請(qǐng)求者對(duì)象需要有命令對(duì)象的成員,并在請(qǐng)求者對(duì)象的方法內(nèi)執(zhí)行命令。 具體命令就是跑1000米,這自然屬于學(xué)生的責(zé)任,所以是具體命令角色的成員方法,而抽象命令類定義這個(gè)命令的抽象接口。 有了上面的分析之后,具體命令模式的實(shí)現(xiàn)代碼如下所示: 1 2 // 教官,負(fù)責(zé)調(diào)用命令對(duì)象執(zhí)行請(qǐng)求 3 public class Invoke 4 { 5 public Command _command; 6 7 public Invoke(Command command) 8 { 9 this._command = command; 10 } 11 12 public void ExecuteCommand() 13 { 14 _command.Action(); 15 } 16 } 17 18 // 命令抽象類 19 public abstract class Command 20 { 21 // 命令應(yīng)該知道接收者是誰(shuí),所以有Receiver這個(gè)成員變量 22 protected Receiver _receiver; 23 24 public Command(Receiver receiver) 25 { 26 this._receiver = receiver; 27 } 28 29 // 命令執(zhí)行方法 30 public abstract void Action(); 31 } 32 33 // 34 public class ConcreteCommand :Command 35 { 36 public ConcreteCommand(Receiver receiver) 37 : base(receiver) 38 { 39 } 40 41 public override void Action() 42 { 43 // 調(diào)用接收的方法,因?yàn)閳?zhí)行命令的是學(xué)生 44 _receiver.Run1000Meters(); 45 } 46 } 47 48 // 命令接收者——學(xué)生 49 public class Receiver 50 { 51 public void Run1000Meters() 52 { 53 Console.WriteLine("跑1000米"); 54 } 55 } 56 57 // 院領(lǐng)導(dǎo) 58 class Program 59 { 60 static void Main(string[] args) 61 { 62 // 初始化Receiver、Invoke和Command 63 Receiver r = new Receiver(); 64 Command c = new ConcreteCommand(r); 65 Invoke i = new Invoke(c); 66 67 // 院領(lǐng)導(dǎo)發(fā)出命令 68 i.ExecuteCommand(); 69 } 70 } 三、.NET中命令模式的應(yīng)用(引用TerryLee)在ASP.NET的MVC模式中,有一種叫Front Controller的模式,它分為Handler和Command樹(shù)兩個(gè)部分,Handler處理所有公共的邏輯,接收HTTP Post或Get請(qǐng)求以及相關(guān)的參數(shù)并根據(jù)輸入的參數(shù)選擇正確的命令對(duì)象,然后將控制權(quán)傳遞到Command對(duì)象,由其完成后面的操作,這里面其實(shí)就是用到了Command模式。
Front Controller 的處理程序部分結(jié)構(gòu)圖
Front Controller的命令部分結(jié)構(gòu)圖 Handler 類負(fù)責(zé)處理各個(gè) Web 請(qǐng)求,并將確定正確的 Command 對(duì)象這一職責(zé)委派給 CommandFactory 類。當(dāng) CommandFactory 返回 Command 對(duì)象后,Handler 將調(diào)用 Command 上的 Execute 方法來(lái)執(zhí)行請(qǐng)求。具體的實(shí)現(xiàn)如下 1 // Handler類 2 public class Handler : IHttpHandler 3 4 { 5 public void ProcessRequest(HttpContext context) 6 7 { 8 9 Command command = CommandFactory.Make(context.Request.Params); 10 11 command.Execute(context); 12 13 } 14 15 public bool IsReusable 16 17 { 18 get 19 20 { 21 return true; 22 } 23 } 24 } 25 26 Command接口: 27 /// <summary> 28 /// Command 29 /// </summary> 30 public interface Command 31 32 { 33 void Execute(HttpContext context); 34 } 35 36 CommandFactory類: 37 /// <summary> 38 /// CommandFactory 39 /// </summary> 40 public class CommandFactory 41 42 { 43 public static Command Make(NameValueCollection parms) 44 45 { 46 47 string requestParm = parms["requestParm"]; 48 49 Command command = null; 50 51 //根據(jù)輸入?yún)?shù)得到不同的Command對(duì)象 52 53 switch (requestParm) 54 55 { 56 case "1": 57 58 command = new FirstPortal(); 59 60 break; 61 62 case "2": 63 64 command = new SecondPortal(); 65 66 break; 67 68 default: 69 70 command = new FirstPortal(); 71 72 break; 73 } 74 75 return command; 76 77 } 78 } 79 80 RedirectCommand類: 81 public abstract class RedirectCommand : Command 82 83 { 84 //獲得Web.Config中定義的key和url鍵值對(duì),UrlMap類詳見(jiàn)下載包中的代碼 85 86 private UrlMap map = UrlMap.SoleInstance; 87 88 protected abstract void OnExecute(HttpContext context); 89 90 public void Execute(HttpContext context) 91 92 { 93 OnExecute(context); 94 95 //根據(jù)key和url鍵值對(duì)提交到具體處理的頁(yè)面 96 97 string url = String.Format("{0}?{1}", map.Map[context.Request.Url.AbsolutePath], context.Request.Url.Query); 98 99 context.Server.Transfer(url); 100 101 } 102 } 103 104 FirstPortal類: 105 public class FirstPortal : RedirectCommand 106 107 { 108 protected override void OnExecute(HttpContext context) 109 110 { 111 //在輸入?yún)?shù)中加入項(xiàng)portalId以便頁(yè)面處理 112 113 context.Items["portalId"] = "1"; 114 115 } 116 } 117 118 SecondPortal類: 119 public class SecondPortal : RedirectCommand 120 121 { 122 protected override void OnExecute(HttpContext context) 123 124 { 125 context.Items["portalId"] = "2"; 126 } 127 } 四、命令模式的適用場(chǎng)景在下面的情況下可以考慮使用命令模式:
五、命令模式的優(yōu)缺點(diǎn)命令模式使得命令發(fā)出的一個(gè)和接收的一方實(shí)現(xiàn)低耦合,從而有以下的優(yōu)點(diǎn):
命令模式的缺點(diǎn):
六、總結(jié)命令模式的實(shí)現(xiàn)要點(diǎn)在于把某個(gè)具體的命令抽象化為具體的命令類,并通過(guò)加入命令請(qǐng)求者角色來(lái)實(shí)現(xiàn)將命令發(fā)送者對(duì)命令執(zhí)行者的依賴分割開(kāi),在上面軍訓(xùn)的例子中,如果不使用命令模式的話,則命令的發(fā)送者將對(duì)命令接收者是強(qiáng)耦合的關(guān)系,實(shí)現(xiàn)代碼如下: 1 // 院領(lǐng)導(dǎo) 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 // 行為的請(qǐng)求者和行為的實(shí)現(xiàn)者之間呈現(xiàn)一種緊耦合關(guān)系 7 Receiver r = new Receiver(); 8 9 r.Run1000Meters(); 10 } 11 } 12 13 public class Receiver 14 { 15 // 操作 16 public void Run1000Meters() 17 { 18 Console.WriteLine("跑1000米"); 19 } 20 } 到這里,本章的內(nèi)容就介紹結(jié)束了,在下一章將繼續(xù)為大家分享下我對(duì)迭代器模式的理解。 |
|