問題出現:
我們在使用C#的抽象類別和介面的時候,往往會遇到以下類似的問題,大致歸納如下:
(1)抽象類別和介面有什麼本質的區別和聯絡?
(2)什麼時候選擇使用抽象類別,然啥時候使用介面最恰當呢?
(3)在項目中怎樣使用才能使得項目更具有可維護性、擴充性?怎樣將它和Struct,類緊密的結合,達到最終的雙刃劍作用?
解決方案:
這也是我在學習抽象類別和介面的時候遇到的問題,從我歸納的這三個問題,不難看出這也許是我們大多數程式員遇到問題的三個階段,
第一階段(基礎概念):就象問題1一樣,這部分人首先需要掃清基礎概念的障礙,首先得懂得什麼叫抽象類別,什麼叫介面?
然後瞭解抽象類別和介面之間的區別和聯絡是什嗎?當然這可能需要一段時間去理解和實踐,畢竟這些概念比較抽象,屬於那種摸不著看不到的東西,當然最主要還是多練習,沒事的時候做個Demo執行個體,把它們都使用一遍,在使用的過程中多想想為什麼要這樣用?這用有什麼好處?能不能使用介面呢,如果不能,使用抽象類別好處又在哪?這樣可以加深對它們的理解,這也是我的一點點經驗吧,呵呵!說了這麼多,我還是把問題1總結一下,一是方便自己記,二是加深理解吧。
抽象類別和介面的概念:其實這些概念在教科書和部落格裡基本上一大堆,前輩們總結的也很好了,但是可能在通俗、易懂方面有點晦澀難懂,我就翻譯一下,加點陝西版的白話文,嘿嘿。
(1)抽象類別:提供了一組衍生類別訪問共用基類的公用方法;
抽象類別的特性是:(1)抽象類別既包括抽象方法,也可以包括方法的實現;(2)抽象類別不能被執行個體化,也不能被密封;(3)抽象類別中的抽象方法要麼在衍生類別中實現,要麼用派生抽象類別繼承(抽象衍生類別可以繼承基類抽象方法的),如果要在衍生類別中實現 基類的抽象方法,必須使用override 修飾符;(4)抽象類別屬於單繼承(這點屬於所有類的同性,在這提一下)(5)抽象類別是一族群的抽象,類似於 IS-A;
以上我如果說的還不是很清楚,給你個官網的關於抽象類別的地址:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract
(2)介面:包含了一組虛方法的抽象類別型;
介面的特性是:(1)介面中只包括虛方法的定義,只有聲明定義,沒有函數實現;(2)介面類中可以包括屬性、事件、索引器等,但不能包括欄位;(3)介面類屬於多繼承;(4)繼承了介面的類必須全部實現介面的方法;
抽象類別和介面的區別和聯絡:
相同點:(1)都是不能直接執行個體化,只能通過繼承方式去實現;
(2)都是對事物行為和對象的抽象,形成一定的設計模式;
不同點:
(1)介面支援多繼承;抽象類別不能實現多繼承;
(2)介面包括方法、屬性、事件、索引器,不能包括欄位;抽象類別可以包括欄位,也可以包括方法的實現;
(3)介面可以支援回調,抽象類別不支援回調
(4)介面可以作為實值型別和參考型別基類,而抽象類別只能作為參考型別的基類;
第二階段(使用階段):就象問題2一樣,這部分人對基礎有了一定的瞭解,但就是缺乏一定的實踐,或許就是做個簡單的Demo了事,那麼什麼時候用抽象類別,啥時用介面呢?
分析第二個問題,我提出3點建議:
第一個建議,對基礎概念不只是概念的記憶,要多練、多思,然後再多練、再多思,迴圈幾次,直到熟爛於心;
第二個建議,盡量在自己的項目中使用這方面的知識,去使用它,你才能發現問題,解決問題,才會思考;
第三個建議,對自己使用過的抽象類別和介面的項目的知識點進行總結和歸納;
就什麼時候使用抽象類別和介面,我總結前輩的經驗,給出以下幾點,僅供參考:
(1)當設計的組件將來有多個版本的時候一般使用抽象類別,例如用C#設計資料庫DB,剛開始你可能使用的是sql server ,mysql,以後大型的項目可能要使用oracle,DB這種大型的資料庫系統,那麼我們在設計類的時候就設計一個抽象的基類DB,讓它具有 資料的一些通用的屬性和方法,屬性:資料庫的串連名,版本,資料庫類型,資料庫的通用方法:Open(),Close()方法等;
(2)當設計的組件同時支援通用的行為動作,可以考慮介面;例如鳥類,人類,車類都可以有聲音,這時候可以設計介面,包含叫的函數行為,然後在各個具體的類中實現;
(3)在繼承了介面的衍生類別或介面中,一旦該介面需要增加行為方法是個比較頭疼的事情,必須所有的繼承都必須實現它的方法,這個時候可以在衍生類別去實現一個新增的介面,來實現衍生類別的獨特動作,舉例說明:
/// <summary>/// 實現一個爬行動物的動作介面/// </summary>interface IAnimalClimb{void Climb();}/// <summary>/// 實現一個會叫的動物的動作介面/// </summary>interface ICry{void Cry();}/// <summary>/// 實現一個動物抽象類別/// </summary>public abstract class Animal{//動物的名字public string Name { get; set; }//動物的顏色public string Color { get; set; }//動物抽象類別的共有方法public abstract void Sleep();public abstract void Breathe();}/// <summary>/// 定義鳥類,通用方法是會飛/// </summary>public class Bird : Animal,ICry{public override void Sleep(){Console.WriteLine("Bird衍生類別繼承了基類的Sleep()方法");}public override void Breathe(){Console.WriteLine("Bird衍生類別繼承了基類的Breathe()方法");}//鳥類可以繼承統一的介面動作,例如:叫public void Cry(){Console.WriteLine("Bird衍生類別繼承了介面ICry的叫的方法");}}/// <summary>/// 定義爬行動物類/// </summary>public class Snake : Animal, IAnimalClimb{public override void Breathe(){Console.WriteLine("Snake衍生類別繼承了基類的Sleep()方法");}public override void Sleep(){Console.WriteLine("Snake衍生類別繼承了基類的Sleep()方法");}//爬行動物可以繼承統一的介面動物,例如:爬public void Climb(){Console.WriteLine("Snake衍生類別繼承了介面IAnimalClimb的爬的方法");}}
以上代碼,只是說明問題,比較簡單;
第三階段(最佳化階段):就象問題3一樣,我們在做一個抽象類別或者介面的時候首先考慮的是能用就行,結果就是定義的類或介面比較多,難以維護和擴充,或者就是類之間有交集,那怎麼最佳化繼承關係?怎樣才能使得程式具有可維護性和擴充性呢?
我個人建議具備以下幾個方面方可:
(1)要有紮實的基礎知識和深厚的基礎功底;
(2)要有一個多問、多思的心;對於抽象類別和介面多問問,為什麼不使用抽象類別而要使用介面?為什麼在這個地方使用介面合適?
(3)多看看前輩們是怎麼設計介面和類的,這方面的資料網上搜搜不少;
(4)個人建議多看看設計模式這方面的知識,因為他們是前輩在設計時的經驗和思想;