前两篇文章循序渐进的介绍了观察者模式、抽象类和接口, 并分析了抽象类和接口的不同.
结尾处有这样的问题:
无论是抽象类还是接口, 都是将设备本身放入了插排的集合中, 那么我们是否可以将此处的参数改为设备的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; 测试了一下 , 已经编译不通过了.
本文通过委托, 将方法作为参数注册到了插排中. 因为安全问题, 又将委托改为了事件.