(转帖)对抽象编程:接口和抽象类
[你必須知道的.NET] 第二回:對抽象編程:接口和抽象類
Author:王濤
Date:2007-4-12
?2007 Anytao.com 轉(zhuǎn)貼請注明出處,留此信息。
?本文將介紹以下內(nèi)容:
- 面向?qū)ο笏枷?#xff1a;多態(tài)
- 接口
- 抽象類
1. 引言
在我之前的一篇post 《抽象類和接口的誰是誰非 》中,和同事管偉的討論,得到很多朋友的關(guān)注,因?yàn)槭遣怀审w系的論道,所以給大家了解造成不便,同時(shí)關(guān)于這個(gè)主題的系統(tǒng)性理論,我認(rèn)為也有必要做以總結(jié),因此才有了本篇的新鮮出爐。同時(shí),我將把上貼中的問題順便也在此做以交代。
2. 概念引入
- ??????什么是接口?
接口是包含一組虛方法的抽象類型,其中每一種方法都有其名稱、參數(shù)和返回值。接口方法不能包含任何實(shí)現(xiàn),CLR 允許接口可以包含事件、屬性、索引器、靜態(tài)方法、靜態(tài)字段、靜態(tài)構(gòu)造函數(shù)以及常數(shù)。但是注意:C# 中不能包含任何靜態(tài)成員。一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,當(dāng)一個(gè)類繼承某個(gè)接口時(shí),它不僅要實(shí)現(xiàn)該接口定義的所有方法,還要實(shí)現(xiàn)該接口從其他接口中繼承的所有方法。
定義方法為:
public?interface?System.IComparable????{????????int?CompareTo(object?o);????}?????public?class?TestCls:?IComparable????{????????public?TestCls()????????{????????}??????????private?int?_value;????????public?int?Value????????{????????????get?{?return?_value;?}????????????set?{?_value?=?value;?}????????}??????????public?int?CompareTo(object?o)????????{?????????????//使用as模式進(jìn)行轉(zhuǎn)型判斷????????????TestCls?aCls?=?o?as?TestCls;????????????if?(aCls?!=?null)????????????{?????????????//實(shí)現(xiàn)抽象方法????????????return?_value.CompareTo(aCls._value);????????????}????????}????}- ???????什么是抽象類?
抽象類提供多個(gè)派生類共享基類的公共定義,它既可以提供抽象方法,也可以提供非抽象方法。抽象類不能實(shí)例化,必須通過繼承由派生類實(shí)現(xiàn)其抽象方法,因此對抽象類不能使用new 關(guān)鍵字,也不能被密封。如果派生類沒有實(shí)現(xiàn)所有的抽象方法,則該派生類也必須聲明為抽象類。另外,實(shí)現(xiàn)抽象方法由overriding 方法來實(shí)現(xiàn)。
定義方法為:
///?<summary>///?定義抽象類///?</summary>abstract?public?class?Animal{????//定義靜態(tài)字段????protected?int?_id;??????//定義屬性????public?abstract?int?Id????{????????get;????????set;????}??????//定義方法????public?abstract?void?Eat();??????//定義索引器????public?string?this[int?i]????{????????get;????????set;????}?}??///?<summary>///?實(shí)現(xiàn)抽象類///?</summary>public?class?Dog:?Animal{????public?override?int?Id????{????????get?{return?_id;}????????set?{_id?=?value;}????}??????public?override?void?Eat()????{????????Console.Write("Dog?Eats.")????}}3. 相同點(diǎn)和不同點(diǎn)
3.1 相同點(diǎn)
- 都不能被直接實(shí)例化,都可以通過繼承實(shí)現(xiàn)其抽象方法。
- 都是面向抽象編程的技術(shù)基礎(chǔ),實(shí)現(xiàn)了諸多的設(shè)計(jì)模式。
3.2 不同點(diǎn)
- 接口支持多繼承;抽象類不能實(shí)現(xiàn)多繼承。
- 接口只能定義抽象規(guī)則;抽象類既可以定義規(guī)則,還可能提供已實(shí)現(xiàn)的成員。
- 接口是一組行為規(guī)范;抽象類是一個(gè)不完全的類,著重族的概念。
- 接口可以用于支持回調(diào);抽象類不能實(shí)現(xiàn)回調(diào),因?yàn)槔^承不支持。
- 接口只包含方法、屬性、索引器、事件的簽名,但不能定義字段和包含實(shí)現(xiàn)的方法;抽象類可以定義字段、屬性、包含有實(shí)現(xiàn)的方法。?
- 接口可以作用于值類型和引用類型;抽象類只能作用于引用類型。例如,Struct 就可以繼承接口,而不能繼承類。
?通過相同與不同的比較,我們只能說接口和抽象類,各有所長,但無優(yōu)略。在實(shí)際的編程實(shí)踐中,我們要視具體情況來酌情量才,但是以下的經(jīng)驗(yàn)和積累,或許能給大家一些啟示,除了我的一些積累之外,很多都來源于經(jīng)典,我相信經(jīng)得起考驗(yàn)。所以在規(guī)則與場合中,我們學(xué)習(xí)這些經(jīng)典,最重要的是學(xué)以致用,當(dāng)然我將以一家之言博大家之笑,看官請繼續(xù)。
3.3 規(guī)則與場合
- 請記住,面向?qū)ο笏枷氲囊粋€(gè)最重要的原則就是:面向接口編程。
- 借助接口和抽象類,23 個(gè)設(shè)計(jì)模式中的很多思想被巧妙的實(shí)現(xiàn)了,我認(rèn)為其精髓簡單說來就是:面向抽象編程。
- 抽象類應(yīng)主要用于關(guān)系密切的對象,而接口最適合為不相關(guān)的類提供通用功能。
- 接口著重于CAN-DO 關(guān)系類型,而抽象類則偏重于IS-A 式的關(guān)系;
- 接口多定義對象的行為;抽象類多定義對象的屬性;?
- 接口定義可以使用public 、protected 、internal?和private 修飾符,但是幾乎所有的接口都定義為public ,原因就不必多說了。
- ?“ 接口不變” ,是應(yīng)該考慮的重要因素。所以,在由接口增加擴(kuò)展時(shí),應(yīng)該增加新的接口,而不能更改現(xiàn)有接口。
- 盡量將接口設(shè)計(jì)成功能單一的功能塊,以.NET Framework 為例,IDisposable 、IDisposable 、IComparable 、IEquatable 、IEnumerable 等都只包含一個(gè)公共方法。
- 接口名稱前面的大寫字母“I” 是一個(gè)約定,正如字段名以下劃線開頭一樣,請堅(jiān)持這些原則。
- 在接口中,所有的方法都默認(rèn)為public 。?
- 如果預(yù)計(jì)會出現(xiàn)版本問題,可以創(chuàng)建“ 抽象類” 。例如,創(chuàng)建了狗(Dog )、雞(Chicken )和鴨(Duck ),那么應(yīng)該考慮抽象出動物(Animal )來應(yīng)對以后可能出現(xiàn)風(fēng)馬牛的事情。而向接口中添加新成員則會強(qiáng)制要求修改所有派生類,并重新編譯,所以版本式的問題最好以抽象類來實(shí)現(xiàn)。
- 從抽象類派生的非抽象類必須包括繼承的所有抽象方法和抽象訪問器的實(shí)實(shí)現(xiàn)。
- 對抽象類不能使用new 關(guān)鍵字,也不能被密封,原因是抽象類不能被實(shí)例化。
- 在抽象方法聲明中不能使用 static 或 virtual 修飾符。
以上的規(guī)則,我就厚顏無恥的暫定為T14 條吧,寫的這么累,就當(dāng)一時(shí)的獎(jiǎng)賞吧。大家也可以互通有無,我將及時(shí)修訂。
4. 經(jīng)典示例
4.1 絕對經(jīng)典
.NET Framework 是學(xué)習(xí)的最好資源,有意識的研究FCL 是每個(gè).NET 程序員的必修課,關(guān)于接口和抽象類在FCL 中的使用,我有以下的建議:
- FCL 對集合類使用了基于接口的設(shè)計(jì),所以請關(guān)注System.Collections 中關(guān)于接口的設(shè)計(jì)實(shí)現(xiàn);
- FCL 對數(shù)據(jù)流相關(guān)類使用了基于抽象類的設(shè)計(jì),所以請關(guān)注System.IO.Stream 類的抽象類設(shè)計(jì)機(jī)制。
4.2?別樣小菜
下面的實(shí)例,因?yàn)槭俏业睦斫?#xff0c;因此給經(jīng)典打上“ 相對” 的記號,至于什么時(shí)候晉升為“ 絕對” ,就看我在.NET 追求的路上,是否能夠一如既往的如此執(zhí)著,因此我將把相對重構(gòu)到絕對為止(呵呵)。 本示例沒有闡述抽象類和接口在設(shè)計(jì)模式中的應(yīng)用,因?yàn)槟菍⑹橇硪黄杏懻搩r(jià)值的文本,本文著眼與概念和原則的把握,但是真正的應(yīng)用來自于具體的需求規(guī)范。
設(shè)計(jì)結(jié)構(gòu)如圖所示:
1.?定義抽象類
public?abstract?class?Animal????{????????protected?string?_name;?????????//聲明抽象屬性????????public?abstract?string?Name????????{????????????get;????????}?????????//聲明抽象方法????????public?abstract?void?Show();?????????//實(shí)現(xiàn)一般方法????????public?void?MakeVoice()????????{????????????Console.WriteLine("All?animals?can?make?voice!");????????}????}2.?定義接口
public?interface?IAction????{????????//定義公共方法標(biāo)簽????????void?Move();????}3. 實(shí)現(xiàn)抽象類和接口
public?class?Duck?:?Animal,?IAction????{????????public?Duck(string?name)????????{????????????_name?=?name;????????}?????????//重載抽象方法????????public?override?void?Show()????????{????????????Console.WriteLine(_name?+?"?is?showing?for?you.");????????}?????????//重載抽象屬性????????public?override?string?Name????????{????????????get?{?return?_name;}????????}?????????//實(shí)現(xiàn)接口方法????????public?void?Move()????????{????????????Console.WriteLine("Duck?also?can?swim.");????????}?????}?????public?class?Dog?:?Animal,?IAction????{????????public?Dog(string?name)????????{????????????_name?=?name;????????}?????????public?override?void?Show()????????{????????????Console.WriteLine(_name?+?"?is?showing?for?you.");????????}?????????public?override?string?Name????????{????????????get?{?return?_name;?}????????}?????????public?void?Move()????????{????????????Console.WriteLine(_name?+?"?also?can?run.");????????}?????}4. 客戶端實(shí)現(xiàn)
public?class?TestAnmial????{????????public?static?void?Main(string?[]?args)????????{????????????Animal?duck?=?new?Duck("Duck");????????????duck.MakeVoice();????????????duck.Show();?????????????Animal?dog?=?new?Dog("Dog");????????????dog.MakeVoice();????????????dog.Show();?????????????IAction?dogAction?=?new?Dog("A?big?dog");????????????dogAction.Move();????????}????}5. 他山之石
正所謂真理是大家看出來的,所以將園子里有創(chuàng)新性的觀點(diǎn)潛列于此,一是感謝大家的共享,二是完善一家之言的不足,希望能夠?qū)㈩I(lǐng)域形成知識,受用于我,受用于眾。
- dunai 認(rèn)為:抽象類是提取具體類的公因式,而接口是為了將一些不相關(guān)的類“ 雜湊” 成一個(gè)共同的群體。至于他們在各個(gè)語言中的句法,語言細(xì)節(jié)并不是我關(guān)心的重點(diǎn)。
- 樺山澗 的收藏也很不錯(cuò)。
- Artech 認(rèn)為:所代碼共用和可擴(kuò)展性考慮,盡量使用Abstract Class 。當(dāng)然接口在其他方面的優(yōu)勢,我認(rèn)為也不可忽視。
- shenfx 認(rèn)為:當(dāng)在差異較大的對象間尋求功能上的共性時(shí),使用接口;當(dāng)在共性較多的對象間尋求功能上的差異時(shí),使用抽象基類。
最后,MSDN 的建議是:
- 如果預(yù)計(jì)要?jiǎng)?chuàng)建組件的多個(gè)版本,則創(chuàng)建抽象類。抽象類提供簡單易行的方法來控制組件版本。通過更新基類,所有繼承類都隨更改自動更新。另一方面,接口一旦創(chuàng)建就不能更改。如果需要接口的新版本,必須創(chuàng)建一個(gè)全新的接口。
- 如果創(chuàng)建的功能將在大范圍的全異對象間使用,則使用接口。抽象類應(yīng)主要用于關(guān)系密切的對象,而接口最適合為不相關(guān)的類提供通用功能。
- 如果要設(shè)計(jì)小而簡練的功能塊,則使用接口。如果要設(shè)計(jì)大的功能單元,則使用抽象類。
- 如果要在組件的所有實(shí)現(xiàn)間提供通用的已實(shí)現(xiàn)功能,則使用抽象類。抽象類允許部分實(shí)現(xiàn)類,而接口不包含任何成員的實(shí)現(xiàn)。
6. 結(jié)論
接口和抽象類,是論壇上、課堂間討論最多的話題之一,之所以將這個(gè)老話題拿出來再議,是因?yàn)閺奈业捏w會來說,深刻的理解這兩個(gè)面向?qū)ο蟮幕緝?nèi)容,對于盤活面向?qū)ο蟮某橄蠡幊趟枷胫陵P(guān)重要。本文基本概況了接口和抽象類的概念、異同和使用規(guī)則,從學(xué)習(xí)的觀點(diǎn)來看,我認(rèn)為這些總結(jié)已經(jīng)足以表達(dá)其核心。但是,對于面向?qū)ο蠛蛙浖O(shè)計(jì)的深入理解,還是建立在不斷實(shí)踐的基礎(chǔ)上,Scott 說自己每天堅(jiān)持一個(gè)小時(shí)用來寫Demo ,那么我們是不是更應(yīng)該勤于鍵盤呢。對于接口和抽象類,請多用而知其然,多想而知其奧吧。
轉(zhuǎn)載于:https://www.cnblogs.com/netstudy0105/archive/2011/03/06/1972423.html
總結(jié)
以上是生活随笔為你收集整理的(转帖)对抽象编程:接口和抽象类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 贴图程序进展
- 下一篇: GChemPaint-绘制化学分子布局