[.net 物件導向程式設計基礎] (12) 物件導向三大特性——繼承
上節我們說了物件導向的三大特性之一的封裝,解決了將對同一物件所能操作的所有資訊放在一起,實現統一對外呼叫,實現了同一物件的複用,降低了耦合。
但在實際應用中,有好多物件具有相同或者相似的屬性,比如有一個物件 果樹(FruitTree),它有成員屬性葉子(Leaf),會開花(Flower),有樹幹(Stem),有樹根(Root),它還會生長(Growth方法)。
有另一個物件蘋果樹(AppleTree)它也是果樹,具有果樹所有特性,那麼我們在程式設計的時候,定義了一個蘋果樹物件,假如再有一個桔子樹(OrangeTree)、桃樹(PeachTree)呢,我們不能一直複製這個物件改名字實現吧?這裡就要用到物件導向的第二個特性:繼承。
1.什麼是繼承
上面的果樹(FruitTree) 和桔樹(OrangeTree)之間是一個“ is-a ”的關係,即我們可以說 “桔樹是果樹”。在物件導向程式設計中,我們把這種關係稱為 “繼承”,即桔樹繼承自果樹。或者說,桔樹類是果樹類的派生類;也可以說果樹是父類,桔樹是子類。同樣的蘋果樹也可以繼承果樹,那麼蘋果樹也可以說是果樹的子類。在這裡我們發現一個類可以有多個派生類,也就是一個類可以被多個類繼承.
通過上面的例項,我們總結一下繼承相關的概念:
(1) 當一個類A能夠獲取另一個類B中所有非私有的資料和操作的定義作為自己的部分或全部成分時,就稱這兩個類之間具有繼承關係。
(2) 被繼承的類B稱為父類或基類,繼承了父類的類A稱為子類或派生類.
2.繼承的特點
上面的例子,假如蘋果樹繼承自果樹,那麼蘋果樹除了具有果樹所有的屬性(葉子,根、花)和方法(生長)之外,蘋果樹還有自己特有的一些屬性,比如有自己的果實蘋果(Apple); 同樣桃樹有自己的果實桃子(Peach),因此繼承的子類可以有自己獨有的成員(屬性或方法等)。
特點一:派生類除了繼承父類的特性外,還可以有自己獨有特性
上面說到的父類果樹(FruitTree)除了有葉子、根、花這些公有的成員之外,也可以有自己的私有成員,比如種類(落葉果樹、常綠果樹),而“種類”這個成員,並不是它的子類蘋果樹(AppleTree)和桔樹(OrangeTree)所具有的,因此是私有成員,子類繼承父類後,並不能擁有父類的私有成員。
特點二:子類不能擁有父類的私有成員
還是上面的例子,假如果樹有一個公有方法生長(Growth),它有兩個子類桃樹和蘋果樹,那麼子類也同時擁有生長這個方法,但是桃樹和蘋果樹的生長過程是不同的,我們可以修改這個方法以適應不同種類果樹的生長。
特點三:子類可以以自己的方式實現父類的功能(即方法重寫,這個在後面專門介紹)
3.繼承的實現
通過上面的例子,我們已經對繼承很熟悉了,拋開概念。簡單的說,繼承一詞本就來源於生活,有財產繼承,精神繼承。物件導向程式設計只不過就是把這些概念抽象化而已,通俗來說就是“蘋果樹是一顆果樹”
程式碼實現上面的例子
1 /// <summary> 2 /// 果樹類 3 /// </summary> 4 class FruitTree 5 { 6 /// <summary> 7 /// 名稱 8 /// 說明:修飾符 protected 保護訪問。只限於本類和子類訪問,例項不能訪問。 9 /// </summary> 10 protected string name; 11 /// <summary> 12 /// 建構函式 13 /// </summary> 14 public FruitTree() 15 { 16 this.name = "無名"; 17 } 18 /// <summary> 19 /// 建構函式二 20 /// </summary> 21 /// <param name="name"></param> 22 public FruitTree(string name) 23 { 24 this.name = name; 25 } 26 object _leaf; 27 object _root; 28 object _flower; 29 string _type; 30 /// <summary> 31 /// 葉子(公有屬性) 32 /// </summary> 33 public object leaf 34 { 35 get { return _leaf; } 36 set { _leaf = value; } 37 } 38 /// <summary> 39 /// 根(公有屬性) 40 /// </summary> 41 public object root 42 { 43 get { return _root; } 44 set { _root = value; } 45 } 46 /// <summary> 47 /// 花(公有屬性) 48 /// </summary> 49 public object flower 50 { 51 get { return _flower; } 52 set { _flower = value; } 53 } 54 /// <summary> 55 /// 類別(不定義修飾符,預設為私有) 56 /// </summary> 57 string type 58 { 59 get { return _type; } 60 set { _type = value; } 61 } 62 63 } 64 65 /// <summary> 66 /// 蘋果樹類 67 /// 繼承自:果樹類 68 /// </summary> 69 class AppleTree:FruitTree 70 { 71 string _myName; 72 /// <summary> 73 /// 建構函式 74 /// 說明:子類呼叫父類同樣的建構函式,需要使用 :base() 75 /// </summary> 76 public AppleTree():base() 77 { 78 } 79 /// <summary> 80 /// 建構函式二 81 /// 說明:子類呼叫父類同樣的建構函式,需要使用 :base(name) 82 /// </summary> 83 /// <param name="name"></param> 84 public AppleTree(string name):base(name) 85 { 86 _myName = name; 87 } 88 89 /// <summary> 90 /// 返回果實的名字 91 /// </summary> 92 /// <returns></returns> 93 public string MyFruitName() 94 { 95 return "我是:" + _myName + ";我的果實叫:蘋果"; 96 } 97 } 98 /// <summary> 99 /// 桔樹類 100 /// 繼承自:果樹類 101 /// </summary> 102 class OrangeTree : FruitTree 103 { 104 string _myName; 105 /// <summary> 106 /// 建構函式 107 /// 說明:子類呼叫父類同樣的建構函式,需要使用 :base() 108 /// </summary> 109 public OrangeTree(): base() 110 { 111 } 112 /// <summary> 113 /// 建構函式二 114 /// 說明:子類呼叫父類同樣的建構函式,需要使用 :base(name) 115 /// </summary> 116 /// <param name="name"></param> 117 public OrangeTree(string name): base(name) 118 { 119 _myName = name; 120 } 121 122 /// <summary> 123 /// 返回果實的名字 124 /// </summary> 125 /// <returns></returns> 126 public string MyFruitName() 127 { 128 return "我是:"+_myName+";我的果實叫:桔子"; 129 } 130 }
呼叫子類:
//呼叫子類 AppleTree appleTree = new AppleTree("蘋果樹"); string myName = appleTree.MyFruitName(); //返回結果為:我是:蘋果樹;我的果實叫:蘋果
//呼叫子類
OrangeTree orangeTree = new OrangeTree("桔子樹");
string myName = orangeTree. MyFruitName ();
//返回結果為:我是:桔子樹;我的果實叫:桔子
通這段程式碼,我們可以看到有了基類果樹,那麼我們再有幾百種樹,只需要一個繼承就可以了,對於子類AppleTree.MyFruitName()返回名字這個方法,在不同子類中可以特有,就是繼承的特點,可以增加特有成員。雖然對於獨有特點需要在每個子類中單獨定義,但是共享父類成員已經讓我們省去不少工作量了,最重要的程式的結構更加清晰、易於維護了。
4.繼承的缺點
看到這個標題,小夥伴們也許很驚訝,既然說了這麼多物件導向繼承特性的好處,原來還有缺點。當然,世界上沒有完美的東西,繼承也是。
缺點一:父類變化,子類不得不變;
缺點二:繼承破壞了包裝,父類的細節暴露給了子類。
前一節說了封裝的獨立特性,是減少了耦合性,而繼承其為了實現複用卻增加了耦合性。
說到這裡小夥伴們糾結了,那麼到底要不要使用繼承,答案是肯定的,它的優點和光芒掩蓋了缺點,也就是說好處更多一些。這裡說明它的缺點,就是提醒我們在使用過程中儘量避免它的缺點所帶來的後果。
那麼要如何才能很好的使用繼承呢?我們應該注意這麼幾點:
a.當兩個物件間是“is a”關係時,可以使用繼承(比如蘋果樹是樹);b.當兩個物件是“as a”關係時,不宜使用繼承(比如手是人的一部分,不能讓手繼承人);
對於繼承的優缺點,我們記住一點:要合理使用繼承,才能發揮最佳效果,而不是盲目使用。
作為物件導向的三大特性之一:繼承,可以說是學好物件導向程式設計的重中之重,因些本節可以說是這個系列的重點,沒有之一。
小夥伴們,又是凌晨了,明天繼續寫。最後按慣例寫幾點使用繼承需要注意的地方。
要點:
1:父類中的私有成員,派生類是絕不能訪問;
2:C#要求一個類只能有一個直接基類;
3:被“sealed”關鍵字修飾的類將不能被繼承;
4:被“protected”修飾的成員或者資料可以直接被派生類訪問,屬於“可以在家族裡分享的祕密”。
5:善用“base”關鍵字,顯示呼叫合適的自定義基類建構函式而不是使用預設建構函式。
6:繼承需要合理使用才能發揮最佳效果,一般情況下適用於“is a”關係,不適用“has a”關係。
==============================================================================================
返回目錄
<如果對你有幫助,記得點一下推薦哦,有不明白的地方或寫的不對的地方,請多交流>
==============================================================================================