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

FlyLolo發表於2018-01-31

 

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

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

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

...待續....

 

上一篇以完成任務式的方式實現了插排的功能.

其中插頭的規範部分值得思考, 上文采用了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方法.

下篇繼續討論..

相關文章