上一篇以完成任务式的方式实现了插排的功能.
其中插头的规范部分值得思考, 上文采用了abstract class的方式,
既然是定义规范, 为什么不用接口方式呢?
一. 下面把上面的例子改造一下, 将原来的abstract class改为接口.
1 public interface IGBElectricalAppliance
2 {
3 void Input(int left, int right);
4 }
5
6 public class OutPut
7 {
8 public OutPut()
9 {
10 this.EACollection = new List<IGBElectricalAppliance>();
11 }
12 private List<IGBElectricalAppliance> EACollection;
13 public void powered(int left,int right)
14 {
15 foreach (var item in EACollection)
16 {
17 item.Input(left,right);
18 }
19 }
20 public void AddInput(IGBElectricalAppliance item)
21 {
22 EACollection.Add(item);
23 }
24
25 public void RemoveInput(IGBElectricalAppliance item)
26 {
27 EACollection.Remove(item);
28 }
29 }
30
31
32
33 public class TV : IGBElectricalAppliance
34 {
35 public void Input(int left, int right)
36 {
37 Show();
38 Sound();
39 }
40
41 private void Show()
42 {
43 Console.WriteLine("I am showing");
44 }
45 private void Sound()
46 {
47 Console.WriteLine("I am sounding");
48 }
49 }
50
51 public class ElectricKettle : IGBElectricalAppliance
52 {
53 public void Input(int left, int right)
54 {
55 Heat();
56 }
57
58 private void Heat()
59 {
60 Console.WriteLine("I am heating");
61 }
62 }
运行一下结果也是一样, 看起来也没啥不同的.
那么到底该用那种方法呢?
二. 现在看一下 abstract class和interface的区别
二者都可以定义一些"规范", 都不可以实例化,
但abstract class中可以有实现的方法, 接口不可以
假如电器有一一些共用的方法例如功率计算(PowerCalculation)等, 可以在abstract class中实现, 非常方便, 这是interface无法实现的, 如下面代码所示
1 public abstract class GBElectricalAppliance
2 {
3 public abstract void Input(int left, int right);
4
5 /// <summary>
6 /// 计算实时功率
7 /// </summary>
8 /// <param name="u">电压</param>
9 /// <param name="i">电流</param>
10 public void PowerCalculation(int u ,int i)
11 {
12 Console.WriteLine("Power:" + (u * i).ToString());
13 }
14
15 //其他通用方法
16 }
从另一个不同来看, interface允许继承多个, 而abstract class不可以.
所以我们也可以这样想, abstract class的含义是"是XX", 反映到例子总就是插座要求插上的设备是国标电器.
如果我们把接口再改一下,
1 public interface IGBElectricalable
2 {
3 void Input(int left, int right);
4 }
5
6 public class Other : IGBElectricalable
7 {
8 public void Input(int left, int right)
9 {
10 doSomeThing();
11 }
12
13 private void doSomeThing()
14 {
15 Console.WriteLine("I am other");
16 }
17 }
只是改了个名字, 大概意思是拥有符合国标标准插头的, 注意这里不再强调是电器了.
例如某些大型机械(上面代码中的Other), 用电部分可能只是辅助, 再定义为电器已经有点不合适了, 它也不需要继承 GBElectricalAppliance.
三.原来的代码调整如下
1 public class OutPut
2 {
3 public OutPut()
4 {
5 this.EACollection = new List<IGBElectricalable>();
6 }
7 private List<IGBElectricalable> EACollection;
8 public void powered(int left,int right)
9 {
10 foreach (var item in EACollection)
11 {
12 item.Input(left,right);
13 }
14 }
15 public void AddInput(IGBElectricalable item)
16 {
17 EACollection.Add(item);
18 }
19
20 public void RemoveInput(IGBElectricalable item)
21 {
22 EACollection.Remove(item);
23 }
24 }
25
26 public abstract class GBElectricalAppliance:IGBElectricalable
27 {
28 public abstract void Input(int left, int right);
29
30 /// <summary>
31 /// 计算实时功率
32 /// </summary>
33 /// <param name="u">电压</param>
34 /// <param name="i">电流</param>
35 public void PowerCalculation(int u, int i)
36 {
37 Console.WriteLine("Power:" + (u * i).ToString());
38 }
39
40 //其他通用方法
41 }
42
43 public class TV : GBElectricalAppliance
44 {
45 public override void Input(int left, int right)
46 {
47 Show();
48 Sound();
49 }
50
51 private void Show()
52 {
53 Console.WriteLine("I am showing");
54 }
55 private void Sound()
56 {
57 Console.WriteLine("I am sounding");
58 }
59 }
60
61 public class ElectricKettle : GBElectricalAppliance
62 {
63 public override void Input(int left, int right)
64 {
65 Heat();
66 }
67
68 private void Heat()
69 {
70 Console.WriteLine("I am heating");
71 }
72 }
原来的TV和Kettle依然可以继承自 GBElectricalAppliance, 这样它们的共用方法 PowerCalculation依然生效,
而GBElectricalAppliance继承了接口 IGBElectricalable
测试一下将Other也插入插排
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 OutPut op = new OutPut();
6 op.AddInput(new TV());
7 op.AddInput(new ElectricKettle());
8 op.AddInput(new Other());
9
10 op.powered(220, 0);
11 }
12 }
可以看到结果中出现了 "I am other".
四:小结
本次用接口的方式对原例子进行了改造, 进一步将插排和插入设备解耦.
文一中, 插排要求插入的设备是符合国标的电器.
本文中, 插排要求插入的设备有符合国标的插头即可, 无论什么样的设备, 无论其是否是电器.
五.思考
由此, 现在进一步想一想, 既然是有符合国标的插头即可.而插头无非就是Input一个方法.
而前两种方式无论是抽象类还是接口, 都是将设备本身放入了插排的集合中,
即 AddInput(IGBElectricalable item), 然后再由插排调用集合中设备的Input方法.
这貌似搞得有点复杂, 耦合度还是高, 而且也不符合实际中直插入插头而不是整个设备的事实.
那么我们是否可以将此处的参数用由插入设备本身(观察者)改为设备的Input方法呢
即 AddInput(Input _input), 然后再由插排直接调用集合中的Input方法.
下篇继续讨论..