青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

面對現實,超越自己
逆水行舟,不進則退
posts - 269,comments - 32,trackbacks - 0

引言

提起Command模式,我想沒有什么比遙控器的例子更能說明問題了,本文將通過它來一步步實現GOF的Command模式。

我們先看下這個遙控器程序的需求:假如我們需要為家里的電器設計一個遠程遙控器,通過這個控制器,我們可以控制電器(諸如燈、風扇、空調等)的開關。我們的控制器上有一系列的按鈕,分別對應家中的某個電器,當我們在遙控器上按下“On”時,電器打開;當我們按下“Off”時,電器關閉。

好了,讓我們開始Command 模式之旅吧。

HardCoding的實現方式

控制器的實現

一般來說,考慮問題通常有兩種方式:從最復雜的情況考慮,也就是盡可能的深謀遠慮,設計之初就考慮到程序的可維護性、擴展性;還有就是從最簡單的情況考慮,不考慮擴展,客戶要求什么,我們就做個什么,至于以后有什么新的需求,等以后再說。當然這兩種方式各有優劣,本文我們從最簡單的情況開始考慮。

我們假設控制器只能控制 三個電器,分別是:燈、電扇、門(你就當是電子門好了^^)。那么我們的控制器應該有三組,共六個按鈕,每一組按鈕分別有“On”,“Off”按鈕。同時,我們規定,第一組按鈕對應燈,第二組按鈕對應電扇,第三組則對應門,那么控制器應該就像這樣:

 

類的設計

好了,控制器大致是這么個樣子了,那么 燈、電扇、門又是什么樣子呢?如果你看過前面幾節的模式,你可能會以為此時又要為它們創建一個基類或者接口,然后供它們繼承或實現。現在讓我們先看看我們想要控制的電器是什么樣子的:

很抱歉,你遺憾地發現,它們的接口完全不同,我們沒有辦法對它們進行抽象,但是因為我們此刻僅考慮客戶最原始的需求(最簡單的情況),那么我們大可以直接將它們復合到 遙控器(ControlPanel) 中

NOTE:關于接口,有狹義的含義:就是一個聲明為interface的類型。還有一個廣義的含義:就是對象暴露給外界的方法、屬性,所以一個抽象類也可以稱作一個接口。這里,說它們的接口不同,意思是說:這三個電器暴露給外界的方法完全不同。


注意到,PressOn方法,它代表著某一個按鍵被按下,并接受一個int類型的參數:SlotNo,它代表是第幾個鍵被按下。顯然,SlotNo的取值為0到2。對于PressOff則是完全相同的設計。

代碼實現

 1 namespace Command {
 2 
 3     // 定義燈
 4     public class Light{
 5        public void TurnOn(){
 6            Console.WriteLine("The light is turned on.");
 7        }
 8        public void TurnOff() {
 9            Console.WriteLine("The light is turned off.");
10        }
11     }
12 
13     // 定義風扇
14     public class Fan {
15        public void Start() {
16            Console.WriteLine("The fan is starting.");
17        }
18        public void Stop() {
19            Console.WriteLine("The fan is stopping.");
20        }
21     }
22 
23     // 定義門
24     public class Door {
25        public void Open() {
26            Console.WriteLine("The door is open for you.");
27        }
28        public void Shut() {
29            Console.WriteLine("The door is closed for safety");
30        }
31     }
32 
33     // 定義遙控器
34     public class ControlPanel {
35        private Light light;
36        private Fan fan;
37        private Door door;
38 
39        public ControlPanel(Light light, Fan fan, Door door) {
40            this.light = light;
41            this.fan = fan;
42            this.door = door;
43        }
44 
45        // 點擊On按鈕時的操作。slotNo,第幾個按鈕被按
46        public void PressOn(int slotNo){
47            switch (slotNo) {
48               case 0:
49                   light.TurnOn();
50                   break;
51               case 1:
52                   fan.Start();
53                   break;
54               case 2:
55                   door.Open();
56                   break;
57            }
58        }
59 
60        // 點擊Off按鈕時的操作。
61        public void PressOff(int slotNo) {
62            switch (slotNo) {
63               case 0:
64                   light.TurnOff();
65                   break;
66               case 1:
67                   fan.Stop();
68                   break;
69               case 2:
70                   door.Shut();
71                   break;
72            }
73        }
74     }
75 
76     class Program {
77        static void Main(string[] args) {
78            Light light = new Light();
79            Fan fan = new Fan();
80            Door door = new Door();
81 
82            ControlPanel panel = new ControlPanel(light, fan, door);
83                       
84            panel.PressOn(0);     // 按第一個On按鈕,燈被打開了
85            panel.PressOn(2);     // 按第二個On按鈕,門被打開了
86            panel.PressOff(2);        // 按第二個Off按鈕,門被關閉了                        
87        }
88     }
89 }
90 
91 輸出為:
92 
93 The light is turned on.
94 The door is open for you.
95 The door is closed for safety

存在問題

這個解決方案雖然能解決當前的問題,但是幾乎沒有任何擴展性可言。或者說,被調用者(Receiver:燈、電扇、門)與它們的調用者(Invoker:遙控器)是緊耦合的。遙控器不僅需要確切地知道它能控制哪些電器,并且需要知道這些電器由哪些方法可供調用。

  • 如果我們需要調換一下按鈕所控制的電器的次序,比如說我們需要讓按鈕1不再控制燈,而是控制門,那么我們需要修改 PressOn 和 PressOff 方法中的Switch語句。
  • 如果我們需要給遙控器多添一個按鈕,以使它多控制一個電器,那么遙控器的字段、構造函數、PressOn、PressOff方法都要修改。
  • 如果我們不給遙控器多添按鈕,但是要求它可以控制10個或者電器,換言之,就是我們可以動態分配某個按鈕控制哪個電器,這樣的設計看上去簡直無法完成。

HardCoding 的另一實現

新設計方案

在考慮新的方案以前,我們先回顧前面的設計,第三個問題似乎暗示著我們的遙控器不夠好,思考一下,我們發現可以這樣設計遙控器:


對比一下,我們看到可以通過左側可以上下活動的閥門來控制當前遙控器控制的是哪個電器(按照圖中當前顯示,控制的是燈),在選定了閥門后,我們可以再通過On,Off按鈕來對電器進行控制。此時,我們需要多添一個方法,通過它來控制閥門(進而選擇想要控制的電器)。我們管這個方法叫做SetDevice()。那么我們的設計變成下圖所示:

NOTE:在圖中,以及現實世界中,閥門所能控制的電器數總是有限的,但在程序中,可以是無限的,就看你有多少個諸如light的電器類了


注意到幾點變化:

  • 因為我們假設遙控器可以控制的電器是無限多的,所以這里不能指定具體電器類型,因為在C#中所有類型均繼承自Object,我們將SetDevice()方法接受的參數設置成為Object。
  • ControlPanel不知道它將控制哪個類,所以圖中ControlPanel和Light、Door、Fan沒有聯系。
  • PressOn()和PressOff()方法不再需要參數,因為很明顯,只有一組On和Off按鈕。

代碼實現

 1 namespace Command {
 2 
 3     public class Light {  // 略   }
 4     public class Fan {    // 略 }
 5     public class Door {   // 略 }
 6 
 7     // 定義遙控器
 8     public class ControlPanel {
 9        private Object device;
10 
11        // 點擊On按鈕時的操作。
12        public void PressOn() {
13            Light light = device as Light;
14            if (light != null) light.TurnOn();
15 
16            Fan fan = device as Fan;
17            if (fan != null) fan.Start();
18 
19            Door door = device as Door;
20            if (door != null) door.Open();
21        }
22 
23        // 點擊Of按鈕時的操作。
24        public void PressOff() {
25            Light light = device as Light;
26            if (light != null) light.TurnOff();
27 
28            Fan fan = device as Fan;
29            if (fan != null) fan.Stop();
30 
31            Door door = device as Door;
32            if (door != null) door.Shut();
33        }
34        
35        // 設置閥門控制哪個電器
36        public void SetDevice(Object device) {
37            this.device = device;
38        }
39     }
40 
41     class Program {
42        static void Main(string[] args) {
43 
44            Light light = new Light();
45            Fan fan = new Fan();  
46 
47            ControlPanel panel = new ControlPanel();
48            
49            panel.SetDevice(light);      // 設置閥門控制燈
50            panel.PressOn();             // 打開燈
51            panel.PressOff();            // 關閉燈
52 
53            panel.SetDevice(fan);    // 設置閥門控制電扇
54            panel.PressOn();             // 打開門
55        }
56     }
57 }

存在問題

我們首先可以看到,這個方案似乎解決了第一種設計的大多數問題,除了一點點瑕疵:

  • 盡管我們可以控制任意多的設備,但是我們每添加一個可以控制的設備,仍需要修改PressOn()和PressOff()方法。
  • 在PressOn()和PressOff()方法中,需要對所有可能控制的電器進行類型轉換,無疑效率低下。

封裝調用

問題分析

我們的處境似乎一籌莫展,想不到更好的辦法來解決。這時候,讓我們先回頭再觀察一下ControlPanel的PressOn()和PressOff()代碼。

 1 // 點擊On按鈕時的操作。
 2 public void PressOn() {
 3     Light light = device as Light;
 4     if (light != null) light.TurnOn();
 5 
 6     Fan fan = device as Fan;
 7     if (fan != null) fan.Start();
 8 
 9     Door door = device as Door;
10     if (door != null) door.Open();
11 }

我們發現PressOn()和PressOff()方法在每次添加新設備時需要作修改,而實際上改變的是對對象方法的調用,因為不管有多少個if語句,只會調用其中某個不為null的對象的一個方法。然后我們再回顧一下OO的思想,Encapsulate what varies(封裝變化)。我們想是不是應該有辦法將這變化的這部分(方法的調用)封裝起來呢?

在考慮如何封裝之前,我們假設已經有一個類,把它封裝起來了,我們管這個類叫做Command,那么這個類該如何使用呢?

我們先考慮一下它的構成,因為它要封裝各個對象的方法,所以,它應該暴露出一個方法,這個方法既可以代表 light.TurnOn(),也可以代表fan.Start(),還可以代表door.Open(),讓我們給這個方法起個名字,叫做Execute()。

好了,現在我們有了Command類,還有了一個萬金油的Execute()方法,現在,我們修改PressOn()方法,讓它通過這個Command類來控制電器(調用各個類的方法)。

1 // 點擊On按鈕時的操作。
2 public void PressOn() {
3     command.Execute();
4 }

哇,是不是有點簡單的過分了!?但就是這么簡單,可我們還是發現了兩個問題:

  1. Command應該能知道它調用的是哪個電器類的哪個方法,這暗示我們Command類應該保存對于具體電器類的一個引用。
  2. 我們的ControlPanel應該有兩個Command,一個Command對應于所有開啟的操作(我們管它叫onCommand),一個Command對應所有關閉的操作(我們管它叫offCommand)。

同時,我們的SetDevice(object)方法,也應該改成SetCommand(onCommand,offCommand)。好了,現在讓我們看看新版ControlPanel 的全景圖吧。

Command類型的實現

顯然,我們應該能看出:onCommand實體變量(instance variable)和offCommand變量屬于Command類型,同時,上面我們已經討論過Command類應該具有一個Execute()方法,除此以外,它還需要可以保存對各個對象的引用,通過Execute()方法可以調用其引用的對象的方法。

那么我們按照這個思路,來看下開燈這一操作(調用light對象的TurnOn()方法)的Command對象應該是什么樣的:

 1 public class LightOnCommand{
 2     Light light;
 3     public Command(Light light){
 4        this.light = light;
 5     }
 6     
 7     public void Execute(){
 8        light.TurnOn();
 9     }
10 }

再看下開電扇(調用fan對象的Start()方法)的Command對象應該是什么樣的:

 1 public class FanStartCommand{
 2     Fan fan;
 3     public Command(Fan fan){
 4        this.fan = fan;
 5     }
 6     
 7     public void Execute(){
 8        fan.Start();
 9     }
10 }

這樣顯然是不行的,它沒有解決任何的問題,因為FanStartCommand和LightOnCommand是不同的類型,而我們的ControlPanel要求對于所有打開的操作應該只接受一個類型的Command的。但是經過我們上面的討論,我們已經知道所有的Command都有一個Execute()方法,我們何不定義一個接口來解決這個問題呢?


OK,現在我們已經完成了全部的設計,讓我們先看一下最終的UML圖,再進行代碼實現吧(簡單起見,只加入了燈和電扇)。


我們先看下這張圖說明了什么,以及發生的順序:

  1. ConsoleApplication,也就是我們的應用程序,它創建電器Fan、Light對象,以及LightOnCommand和FanStartCommand。
  2. LightOnCommand、FanStartCommand實現了ICommand接口,它保存著對于Fan和Light的引用,并通過Execute()調用Fan和Light的方法。
  3. ControlPanel復合了Command對象,通過調用Command的Execute()方法,間接調用了Light的TurnOn()方法或者是Fan的Stop()方法。

它們之間的時序圖是這樣的:


可以看出:通過引入Command對象,ControlPanel對于它實際調用的對象Fan或者Light是一無所知的,它只知道當On按下的時候就調用onCommand的Execute()方法;當Off按下的時候就調用offCommand的Execute()方法。Light和Fan當然更不知道誰在調用它。通過這種方式,我們實現了調用者(Invoker,遙控器ControlPanel) 和 被調用者(Receiver,電扇Fan等)的解耦。如果將來我們需要對這個ControlPanel進行擴展,只需要再添加一個實現了ICommand接口的對象就可以了,對于ControlPanel無需做任何修改。

代碼實現

 1 namespace Command {
 2 
 3     // 定義空調,用于測試給遙控器添新控制類型
 4     public class AirCondition {
 5        public void Start() {
 6            Console.WriteLine("The AirCondition is turned on.");
 7        }
 8        public void SetTemperature(int i) {
 9            Console.WriteLine("The temperature is set to " + i);
10        }
11        public void Stop() {
12            Console.WriteLine("The AirCondition is turned off.");
13        }
14     }
15     
16     // 定義Command接口
17     public interface ICommand {
18        void Execute();
19     }
20 
21     // 定義開空調命令
22     public class AirOnCommand : ICommand {
23        AirCondition airCondition;
24        public AirOnCommand(AirCondition airCondition) {
25            this.airCondition = airCondition;
26        }
27        public void Execute() {  //注意,你可以在Execute()中添加多個方法
28            airCondition.Start();
29            airCondition.SetTemperature(16);
30        }
31     }
32 
33     // 定義關空調命令
34     public class AirOffCommand : ICommand {
35        AirCondition airCondition;
36        public AirOffCommand(AirCondition airCondition) {
37            this.airCondition = airCondition;
38        }
39        public void Execute() {
40            airCondition.Stop();
41        }
42     }
43 
44 
45     // 定義遙控器
46     public class ControlPanel {
47        private ICommand onCommand;
48        private ICommand offCommand;
49 
50        public void PressOn() {
51            onCommand.Execute();
52        }
53 
54        public void PressOff() {
55            offCommand.Execute();
56        }
57 
58        public void SetCommand(ICommand onCommand,ICommand offCommand) {
59            this.onCommand = onCommand;
60            this.offCommand = offCommand;
61        }
62     }
63 
64     class Program {
65        static void Main(string[] args) {
66 
67            // 創建遙控器對象
68            ControlPanel panel = new ControlPanel();
69 
70            AirCondition airCondition = new AirCondition();       //創建空調對象
71 
72            // 創建Command對象,傳遞空調對象
73            ICommand onCommand = new AirOnCommand(airCondition);
74            ICommand offCommand = new AirOffCommand(airCondition);
75 
76            // 設置遙控器的Command
77            panel.SetCommand(onCommand, offCommand);
78 
79            panel.PressOn();      //按下On按鈕,開空調,溫度調到16度
80            panel.PressOff();     //按下Off按鈕,關空調
81 
82        }
83     }
84 }

 

Command 模式

實際上,我們上面做的這一切,實現了另一個設計模式:Command模式。現在又到了給出官方定義的時候了。每次到了這部分我就不知道該怎么寫了,寫的人太多了,資料也太多了,我相信你看到這里對Command模式已經比較清楚了,所以我還是一如既往地從簡吧。

Command模式的正式定義:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可撤消的操作。

它的 靜態圖 是這樣的:


它的 時序圖 是這樣的:


可以和我們前面的圖對比一下,對于這兩個圖,除了改了個名字外基本沒變,我就不再說明了,也留給你一點思考的空間。

總結

本文簡單地介紹了GOF的Commmand模式,我們通過一個簡單的范例家電遙控器 實現了這一模式。

我們首先了解了不使用此模式的HardCoding方式的實現方法,討論了它的缺點;然后又換了另一種改進了的實現方法,再次討論了它的不足。  然后,我們通過將對象的調用封裝到一個Command對象中的方式,巧妙地完成了設計。最后,我們給出了Command模式的正式定義。

本文僅僅簡要介紹了Command模式,它的高級應用:取消操作(UnDo)、事務支持(Transaction)、隊列請求(Queuing Request) 以后有時間了會再寫文章。

希望這篇文章能對你有所幫助!

本文轉自:http://www.tracefact.net/Design-Pattern/Command.aspx
其他鏈接:http://blog.csdn.net/tianxiaoqi2008/article/details/7276846

posted on 2012-07-10 13:40 王海光 閱讀(533) 評論(0)  編輯 收藏 引用 所屬分類: Design Pattern
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美不卡激情三级在线观看| 欧美成人精品一区二区三区| 黄色精品在线看| 国产免费成人av| 国产精品黄页免费高清在线观看| 欧美日韩精品一区二区三区四区| 欧美日韩影院| 国产日韩欧美在线播放不卡| 黄色另类av| 亚洲精品乱码久久久久久黑人| 最新高清无码专区| 亚洲视频在线看| 久久精品二区亚洲w码| 久久久久欧美精品| 欧美成人一区二区三区| 亚洲美女中出| 欧美一区二区视频97| 免播放器亚洲| 国产精品免费在线| 亚洲国产日韩欧美一区二区三区| 日韩午夜一区| 久久久久久久久久久成人| 欧美国产日韩亚洲一区| 亚洲美女av在线播放| 亚洲欧美国产三级| 久久综合色一综合色88| 欧美三区美女| 亚洲高清二区| 欧美一区二区精品久久911| 一区二区三区在线观看视频| 99国产精品久久久久久久| 欧美一区二区| 亚洲精品一区二区在线观看| 久久精品青青大伊人av| 国产精品分类| 亚洲美女淫视频| 毛片av中文字幕一区二区| 亚洲婷婷在线| 欧美黄色aa电影| 国模一区二区三区| 午夜在线观看欧美| 最新国产成人在线观看| 性做久久久久久| 亚洲日本va午夜在线电影| 一本到12不卡视频在线dvd| 亚洲在线中文字幕| 亚洲一区二区免费视频| 美女黄毛**国产精品啪啪| 国产精品麻豆va在线播放| 亚洲精品一区在线观看香蕉| 久久深夜福利免费观看| 亚洲一区欧美二区| 欧美午夜宅男影院在线观看| 在线看片欧美| 欧美一区二区三区在线| 中国女人久久久| 欧美区一区二区三区| 亚洲国产经典视频| 免费成人美女女| 久久久不卡网国产精品一区| 国产亚洲在线观看| 久久久久国产一区二区| 性欧美办公室18xxxxhd| 国产日韩欧美亚洲一区| 久久成人精品无人区| 亚洲综合清纯丝袜自拍| 国产精品久久一级| 午夜久久99| 亚洲中字黄色| 国产一区在线视频| 久久伊人精品天天| 久久中文久久字幕| 亚洲精品在线一区二区| 亚洲高清视频一区| 免费成人在线视频网站| 91久久久久久| 亚洲另类自拍| 亚洲免费成人av| 国产精品成人国产乱一区| 先锋影音一区二区三区| 欧美一级一区| 亚洲精品在线看| 中文精品视频一区二区在线观看| 国产精品久久一卡二卡| 久久久精品五月天| 蜜臀av在线播放一区二区三区| 亚洲精品久久久久中文字幕欢迎你| 亚洲精品一线二线三线无人区| 欧美性猛交一区二区三区精品| 欧美一区二区大片| 免费久久99精品国产| 亚洲一区二区av电影| 欧美在线中文字幕| 亚洲激情在线播放| 亚洲性xxxx| 91久久久在线| 一区二区高清视频在线观看| 国产视频在线观看一区| 亚洲国产精品女人久久久| 欧美午夜精品电影| 久久久综合网| 欧美日韩在线大尺度| 久久久久久久一区二区| 欧美激情一区在线| 久久久久青草大香线综合精品| 欧美精品一区二区蜜臀亚洲| 欧美与黑人午夜性猛交久久久| 亚洲欧美在线高清| 亚洲国产精品一区二区www在线| 一个色综合av| 亚洲人成人99网站| 久久狠狠一本精品综合网| 99在线热播精品免费| 欧美在线不卡视频| 亚洲免费在线视频| 欧美激情一区二区三区在线视频观看 | 欧美超级免费视 在线| 亚洲在线一区二区| 美国十次成人| 久久精品国产第一区二区三区最新章节 | 欧美在线观看一二区| 日韩亚洲一区在线播放| 久久嫩草精品久久久精品| 性欧美1819sex性高清| 欧美精品一区视频| 欧美激情在线免费观看| 国产一区清纯| 先锋影院在线亚洲| 欧美一区二区精美| 国产精品久久久久久久午夜 | 久久久久久综合| 欧美亚洲免费| 欧美色图五月天| 亚洲理论电影网| av成人免费| 欧美人交a欧美精品| 亚洲电影毛片| 日韩天堂av| 欧美日韩精品久久| 亚洲免费大片| 亚洲欧美久久| 欧美性一区二区| 亚洲一区二区三区在线视频| 亚洲一区二区三区四区中文| 欧美日韩综合另类| 亚洲一区精彩视频| 久久激情婷婷| 狠狠色噜噜狠狠色综合久| 久久久久青草大香线综合精品| 欧美中文日韩| 国内精品嫩模av私拍在线观看| 久久精品二区| 欧美激情第8页| 日韩亚洲视频| 国产精品日韩精品欧美在线| 亚洲欧美综合另类中字| 久久综合狠狠综合久久综青草| 国产精品一区久久| 在线不卡a资源高清| 亚洲欧美日韩综合国产aⅴ| 羞羞色国产精品| 国内自拍视频一区二区三区| 狼人天天伊人久久| 亚洲欧洲在线看| 在线亚洲自拍| 国模叶桐国产精品一区| 欧美成人中文字幕| 亚洲午夜精品视频| 国产精品久久久久久久电影| 欧美在线观看一区二区| 欧美gay视频| 亚洲一区二区精品在线| 国产一区久久| 欧美日韩精品一区视频| 欧美在线视频a| 亚洲精品一级| 欧美一区二区三区四区在线 | 久久久女女女女999久久| 亚洲精品免费看| 老司机午夜精品视频在线观看| 一区二区日韩伦理片| 国产一区二区三区在线播放免费观看 | 欧美亚洲成人网| 久久一区二区三区av| 99精品视频免费观看| 欧美电影在线观看| 久久久精品视频成人| 亚洲午夜av在线| 亚洲精品乱码久久久久久按摩观| 国产精品亚洲成人| 欧美日韩四区| 欧美另类一区| 欧美福利精品| 久久久久久九九九九| 亚洲欧美国产制服动漫| 99国产精品久久久久久久久久| 欧美国产日韩一区二区在线观看| 久久精品91| 久久精品av麻豆的观看方式| 欧美一级黄色录像|