一,、前言之前一直在忙于工作上的事情,關(guān)于設(shè)計(jì)模式系列一直沒更新,,最近項(xiàng)目中發(fā)現(xiàn),,對于設(shè)計(jì)模式的了解是必不可少的,當(dāng)然對于設(shè)計(jì)模式的應(yīng)用那更是重要,,可以說是否懂得應(yīng)用設(shè)計(jì)模式在項(xiàng)目中是衡量一個(gè)程序員的技術(shù)水平,,因?yàn)閷τ谝粋€(gè)功能的實(shí)現(xiàn),高級工程師和初級工程師一樣都會(huì)實(shí)現(xiàn),,但是區(qū)別在于它們實(shí)現(xiàn)功能的可擴(kuò)展和可維護(hù)性,,也就是代碼的是否“優(yōu)美”、可讀,。但是,,要更好地應(yīng)用,首先就必須了解各種設(shè)計(jì)模式和其應(yīng)用場景,,所以我還是希望繼續(xù)完成設(shè)計(jì)模式這個(gè)系列,,希望通過這種總結(jié)的方式來加深自己設(shè)計(jì)模式的理解。 二,、命令模式的介紹2.1 命令模式的定義命令模式屬于對象的行為型模式,。命令模式是把一個(gè)操作或者行為抽象為一個(gè)對象中,通過對命令的抽象化來使得發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分隔開,。命令模式的實(shí)現(xiàn)可以提供命令的撤銷和恢復(fù)功能,。 2.2 命令模式的結(jié)構(gòu)既然,命令模式是實(shí)現(xiàn)把發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開,,然而中間必須有某個(gè)對象來幫助發(fā)出命令者來傳達(dá)命令,,使得執(zhí)行命令的接收者可以收到命令并執(zhí)行命令。例如,,開學(xué)了,院領(lǐng)導(dǎo)說計(jì)算機(jī)學(xué)院要進(jìn)行軍訓(xùn),,計(jì)算機(jī)學(xué)院的學(xué)生要跑1000米,,院領(lǐng)導(dǎo)的話也就相當(dāng)于一個(gè)命令,,他不可能直接傳達(dá)給到學(xué)生,他必須讓教官來發(fā)出命令,,并監(jiān)督學(xué)生執(zhí)行該命令,。在這個(gè)場景中,發(fā)出命令的責(zé)任是屬于學(xué)院領(lǐng)導(dǎo),,院領(lǐng)導(dǎo)充當(dāng)與命令發(fā)出者的角色,,執(zhí)行命令的責(zé)任是屬于學(xué)生,學(xué)生充當(dāng)于命令接收者的角色,,而教官就充當(dāng)于命令的發(fā)出者或命令請求者的角色,,然而命令模式的精髓就在于把每個(gè)命令抽象為對象。從而命令模式的結(jié)構(gòu)如下圖所示: 從命令模式的結(jié)構(gòu)圖可以看出,,它涉及到五個(gè)角色,,它們分別是:
2.3 命令模式的實(shí)現(xiàn)現(xiàn)在,,讓我們以上面的軍訓(xùn)的例子來實(shí)現(xiàn)一個(gè)命令模式,在實(shí)現(xiàn)之前,,可以參考下命令模式的結(jié)構(gòu)圖來分析下實(shí)現(xiàn)過程,。 軍訓(xùn)場景中,具體的命令即是學(xué)生跑1000米,,這里學(xué)生是命令的接收者,,教官是命令的請求者,院領(lǐng)導(dǎo)是命令的發(fā)出者,,即客戶端角色,。要實(shí)現(xiàn)命令模式,則必須需要一個(gè)抽象命令角色來聲明約定,,這里以抽象類來來表示,。命令的傳達(dá)流程是: 命令的發(fā)出者必須知道具體的命令、接受者和傳達(dá)命令的請求者,,對應(yīng)于程序也就是在客戶端角色中需要實(shí)例化三個(gè)角色的實(shí)例對象了,。 命令的請求者負(fù)責(zé)調(diào)用命令對象的方法來保證命令的執(zhí)行,對應(yīng)于程序也就是請求者對象需要有命令對象的成員,,并在請求者對象的方法內(nèi)執(zhí)行命令,。 具體命令就是跑1000米,這自然屬于學(xué)生的責(zé)任,所以是具體命令角色的成員方法,,而抽象命令類定義這個(gè)命令的抽象接口,。 有了上面的分析之后,具體命令模式的實(shí)現(xiàn)代碼如下所示: 1 2 // 教官,,負(fù)責(zé)調(diào)用命令對象執(zhí)行請求 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)該知道接收者是誰,,所以有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樹兩個(gè)部分,,Handler處理所有公共的邏輯,接收HTTP Post或Get請求以及相關(guān)的參數(shù)并根據(jù)輸入的參數(shù)選擇正確的命令對象,,然后將控制權(quán)傳遞到Command對象,,由其完成后面的操作,這里面其實(shí)就是用到了Command模式,。
Front Controller 的處理程序部分結(jié)構(gòu)圖
Front Controller的命令部分結(jié)構(gòu)圖 Handler 類負(fù)責(zé)處理各個(gè) Web 請求,,并將確定正確的 Command 對象這一職責(zé)委派給 CommandFactory 類。當(dāng) CommandFactory 返回 Command 對象后,,Handler 將調(diào)用 Command 上的 Execute 方法來執(zhí)行請求,。具體的實(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對象 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鍵值對,UrlMap類詳見下載包中的代碼 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鍵值對提交到具體處理的頁面 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以便頁面處理 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 } 四,、命令模式的適用場景在下面的情況下可以考慮使用命令模式:
五、命令模式的優(yōu)缺點(diǎn)命令模式使得命令發(fā)出的一個(gè)和接收的一方實(shí)現(xiàn)低耦合,,從而有以下的優(yōu)點(diǎn):
命令模式的缺點(diǎn):
六,、總結(jié)命令模式的實(shí)現(xiàn)要點(diǎn)在于把某個(gè)具體的命令抽象化為具體的命令類,,并通過加入命令請求者角色來實(shí)現(xiàn)將命令發(fā)送者對命令執(zhí)行者的依賴分割開,在上面軍訓(xùn)的例子中,,如果不使用命令模式的話,,則命令的發(fā)送者將對命令接收者是強(qiáng)耦合的關(guān)系,實(shí)現(xiàn)代碼如下: 1 // 院領(lǐng)導(dǎo) 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 // 行為的請求者和行為的實(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ù)為大家分享下我對迭代器模式的理解,。 |
|