...待續....
上一篇以完成任務式的方式實現了插排的功能.
其中插頭的規範部分值得思考, 上文采用了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方法.
下篇繼續討論..