接口與抽象類是面向對象編程中兩個非常重要的角色,二者各自起著非常重要的作用。但是很多初學的朋友往往會對使用接口還是抽象類存在的很大的迷惑。就我自己的一點心得,發表一下拙見。面向對象的一些回顧:
面向對象世界中有一個古老的法則:接口隔離原則,指的是不要把多個功能全部都集中在一個接口裡面。接口實現的功能要相對單一;衍生開來可以得到另外一個結論:對一組或者稱一系列功能的實現,盡量定義相對功能單一的小模塊來實現這一組功能。這其實也是解耦和的體現。
那這跟我們的接口和抽象類有什麼關系呢?那又得擺出另外一個法則:依賴倒置原則,針對接口編程而不是針對實現編程。
說到這,又會有一個新的問題蹦出來,這是自相矛盾啊,既然要針對接口編程還要抽象類干嗎使?我們經常說面向對象,面向對象是來源於生活的。是人們要把對現實世界中的一系列方法論應用到程序設計當中來。 從對象這一概念的引入我們就可以揣摩這一點。人類社會中有很多對象的概念,人、車、物體。不幸的是用程序來實現這些對象比在概念上定義對象要難很多。
(如果能達成這一共識,您可以繼續往下看,否則就請看官您移步至留言討論吧)
MS給出開發者的建議是,用抽象類來實現接口。子類再繼承基類。
實例說明:
為什麼要這麼建議?OK,我們試著結合實際來說明一下這個問題吧。我們要造車。這個車有個基本的屬性就是能移動、還必須有輪子。那我們就設計一個接口
1public interface ICar
2 {
3 string Wheel
4 {
5 get;
6 set;
7 }
8 void Move();
9 }
10
接下來的事情,就是實現了。造什麼車都行,繼承一下就行。隨著科技的發展,我們的車想要飛了。此時當然不能修改這個接口,因為要遵循開閉原則。為什麼要遵循?我們可以想一下,人坐上飛機能飛上天。但是也沒見誰認為人有會飛這個特性的。那也好辦,不許修改,那我再加一個接口。
1interface IFlyable
2 {
3 void Fly();
4 }
5好,我們的飛行汽車最後應該是這樣的。
1class FlyCar : ICar,IAerocraft
2 {
3 private string wheel = string.Empty;
4
5 public void Fly()
6 {
7 Console.WriteLine("{0}車飛起來了",this.wheel);
8 }
9 public string Engine
10 {
11 get
12 {
13 return wheel;
14 }
15 set
16 {
17 wheel = value;
18 }
19 }
20
21 public void Move()
22 {
23 Console.WriteLine("{0}輪車在走",this.wheel);
24 }
25 }
26 看起來很不錯,車能飛能走了。那它現在他的祖宗到底車還是飛行器呢?我們自己在心裡辯論一下吧。估計不是很容易辯清楚。
我們前面說過,面向對象的思想來源於現實生活。如果把這組例子引入到現實中來,造會飛的汽車。肯定是要在原有的汽車上面下功夫。比如你裝上噴氣動力裝置,或者裝上翅膀。這只屬於擴展功能,而不能說是繼承基類。但上面的例子可以明顯的看出,我們的飛行汽車已經成了雜交品種。分不出到底是車還是飛行器了。這裡就可以知道為什麼C#和JAVA都不支持多重繼承基類了。避免雜交,減少耦合。
上面把車定義成接口並不完美,我們知道,一輛正常的車肯定能移動。這是天生的本質,不需要任何實現。但是上面還需要子類來實現這個功能。從這一點其實可以衍生出很多問題來。我們這裡不做過多討論。
重新設計這個系統。我們可以把移動,飛行都看成是一種行為。我們的車本身擁有Move這個行為,是構成車基類的基本要素。
1interface IMoveable
2 {
3 void Move();
4 }
5 interface IFlyable
6 {
7 void Fly();
8 }
9public abstract class Car : IMoveable
10 {
11 public abstract string Wheel
12 {
13 get;
14 set;
15 }
16 public virtual void Move()
17 {
18 Console.WriteLine("車移動了");
19 }
20 }
21 public sealed class FlyCar : Car,IFlyable
22 {
23 private string wheel = string.Empty;
24 public override string Wheel
25 {
26 get
27 {
28 return wheel;
29 }
30 set
31 {
32 wheel = value;
33 }
34 }
35
36 public void Fly()
37 {
38 base.Move();
39 Console.WriteLine("汽車起飛成功!");
40 }
41 }
42 //在這裡應用任何模式都很簡單了
43 static void Main(string[] args)
44 {
45 FlyCar c = new FlyCar();
46 ((IFlyable)c).Fly();
47 ((Car)c).Move();
48 }
49
總結歸納: 其實類似的例子在我們的.NET Library裡隨處可見,例如Control類是繼承於Component和其他大量的接口的,而他們的基類卻是MarshalByRefObject。因為他們歸功到底又屬於引用對象。
從上面的描述中,我們可以得出結論:
接口:是某類行為或功能的抽象。是一種開關或者是契約。所以從字面上來理解就非常清楚了,西方神話中有很多和魔鬼定下契約來使自己的力量得到提升的故事。你必須定下這個契約才能得到你想要的力量。
抽象類:對具體對象的最高抽象,這個對象擁有自己的最基本特征。
所以,從整體上來講,抽象類和接口本質上都是是系統的最高抽象。從實際上來講,二者抽象的對象不一樣,就這一點導致了他們二者的應用的截然不同。