一個插排引發的設計思想 (三) 委託與事件

FlyLolo發表於2018-02-01

 

一個插排引發的設計思想 (一) 觀察者模式

一個插排引發的設計思想 (二) 抽象類與介面

一個插排引發的設計思想 (三) 委託與事件

...待續....

 

前兩篇文章循序漸進的介紹了觀察者模式、抽象類和介面, 並分析了抽象類和介面的不同.

結尾處有這樣的問題:

無論是抽象類還是介面, 都是將裝置本身放入了插排的集合中, 那麼我們是否可以將此處的引數改為裝置的Input方法呢?

那麼我們就用到了委託Delegate.

1    public delegate void InputDelegate(int left, int right);

看到接下來的程式碼, 老鳥別笑, 請允許我"循序漸進"的引導著思路重構.

我們用委託InputDelegate定義了插頭的標準. 那麼原來的程式碼改變一下

一. 用方法作為引數傳遞, 代替原來的整個裝置

 1     public delegate void InputDelegate(int left, int right);
 2     public class OutPut
 3     {
 4         public OutPut()
 5         {
 6             this.EACollection = new List<InputDelegate>();
 7         }
 8         private List<InputDelegate> EACollection;
 9         public void powered(int left, int right)
10         {
11             foreach (var item in EACollection)
12             {
13                 item(left, right);
14             }
15         }
16         public void AddInput(InputDelegate item)
17         {
18             EACollection.Add(item);
19         }
20 
21         public void RemoveInput(InputDelegate item)
22         {
23             EACollection.Remove(item);
24         }
25     }
26 
27     class Program
28     {
29         static void Main(string[] args)
30         {
31             OutPut op = new OutPut();
32             op.AddInput(new TV().Input);
33             op.AddInput(new ElectricKettle().InputAAAA);
34 
35             op.powered(220, 0);
36 
37             Console.ReadKey();
38         }
39     }

原來的TV和ElectricKettle無需任何繼承任何抽象類和介面, 只要有和定義的Delegate一樣的方法簽名的方法即可.

甚至名字都可以不一樣, 例如ElectricKettle的input方法我隨便改了一下改成了InputAAAA依然沒問題.

 1     public class ElectricKettle
 2     {
 3         public void InputAAAA(int left, int right)
 4         {
 5             Heat();
 6         }
 7 
 8         private void Heat()
 9         {
10             Console.WriteLine("I am heating");
11         }
12     }

通過上面的方法, 我們把插入插排的引數由整個裝置改成了裝置的插頭. 

功能是實現了, 但Delegate用起來沒必要那麼麻煩, 我們繼續改

二. 用+=和-=來操作

我們改造一下Output類

 1     public delegate void InputDelegate(int left, int right);
 2     public class OutPut
 3     {
 4         public InputDelegate inputDelegate;
 5         public void powered(int left, int right)
 6         {
 7             inputDelegate(left, right);
 8         }
 9     }
10 
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             OutPut op = new OutPut();
16             op.inputDelegate += new TV().Input;
17             op.inputDelegate += new ElectricKettle().InputAAAA;
18 
19             op.powered(220, 0);
20 
21             Console.ReadKey();
22         }
23     }

簡潔多了, 根據Delegate的特性, 插排集本身也被inputDelegate代替了.

在呼叫的時候, 我們只需將input方法 +=到該inputDelegate即可.

三. 委託和事件

上面的例子貌似已經很好了, 但既然是插排, 也就是可能會有好多插頭來插拔, 而插頭之間互不干涉, 呼叫的位置可能存在於系統的任何位置 .

但上面的程式碼讓我們想到一個問題,

我們把"集和"暴露出來了, 之前的 private List<IGBElectricalable> EACollection是私有的, 只可以通過add和 remove兩個方法操作.

現在我們把它public了,  哪個搗蛋的寫了一句op.inputDelegate = null,  把插排都弄沒了, 讓別的插頭怎麼辦.

所以我們還希望像原來那樣只提供增減的方法, 不允許賦值, 這裡我們就用到了事件.

 1     public delegate void InputDelegate(int left, int right);
 2     
 3     public class OutPut
 4     {
 5         public event InputDelegate inputEvent;
 6         public void powered(int left, int right)
 7         {
 8             inputEvent(left, right);
 9         }
10     }
11 
12     class Program
13     {
14         static void Main(string[] args)
15         {
16             OutPut op = new OutPut();
17             
18             op.inputEvent += new TV().Input;
19             op.inputEvent += new ElectricKettle().InputAAAA;
20             //op.inputEvent = null;
21             op.powered(220, 0);
22 
23             Console.ReadKey();
24         }
25     }

Output中的 inputDelegate 改為了 inputEvent, inputEvent不再允許通過=來賦值了.

main方法中註釋的一行 //op.inputEvent = null; 測試了一下 , 已經編譯不通過了.

四. 小結

本文通過委託, 將方法作為引數註冊到了插排中.  因為安全問題, 又將委託改為了事件.

 

相關文章