譯文:伯仲之爭--接口與抽象類【節(jié)選】

原文地址:http://www.codeproject.com/Articles/814367/Interfaces-and-Abstract-Classes

【譯者注】
? ??網(wǎng)上有很多關(guān)于這兩弟兄掐架比高低的文章坪仇,但個人認(rèn)為這篇文章解析非常具體昏苏,而且提到了很多人沒有分析到的點(diǎn)吱抚,特別是從接口的“使用者”堕虹,“開發(fā)者”不同的角度來分析接口與抽象類的差異固耘,實(shí)在精彩筹麸!原文很長耸携,就只能節(jié)選一部分了纵诞。


前提介紹

? ??我經(jīng)趁Ω桑看見有人問關(guān)于接口與抽象類的區(qū)別器予,而且大部分的回答都只是在關(guān)注他們各自的外在特點(diǎn),并沒有對如何使用進(jìn)行進(jìn)一步解釋捐迫。

? ??比如乾翔,大部分的解釋像這樣:
1. 抽象類有具體實(shí)現(xiàn),而接口卻沒有施戴;
2. 在.NET中我們沒辦法進(jìn)行多繼承反浓,但可以同時實(shí)現(xiàn)多個接口;
3. 接口就是一個協(xié)議赞哗,而抽象類的內(nèi)容則遠(yuǎn)遠(yuǎn)不止于此(這句話說對我來說簡直毫無意義雷则,但是卻是非常常見的解釋)

? ??除了這些外,其實(shí)還有很多答案都和這些大同小異肪笋。換句話說月劈,不管這些答案各自到底正確與否,總之是沒有完全答到點(diǎn)上(當(dāng)然有時候問題也問得不夠明確)涂乌。

? ? ? ?那到底我們該什么時候用接口艺栈,什么時候用抽象類?


只在乎傳參調(diào)用 -- 接口是個好東西

? ??假設(shè)你正在開發(fā)一個系統(tǒng)湾盒,而且這個系統(tǒng)目前需要有日志記錄功能湿右。至于這個日志記錄是否與整體業(yè)務(wù)邏輯有關(guān),這個我不會去在意罚勾,我準(zhǔn)備和大家探討的毅人,是這個日志的不同記錄方式:有的寫成文件吭狡,有的寫到數(shù)據(jù)庫里,有的則有其他更多的記錄方式等等丈莺。

? ??這樣一來划煮,你可以很容易想到它大概應(yīng)該是這個樣子:

logger.Log("An error happened in module X.");
logger.ConcatLog("An error happened in module ", moduleName, ".");
logger.FormatLog("An error happened in module {0}.", moduleName);

? ? ? ?如果各位覺得還不夠清楚,我進(jìn)一步說明一下:第一個Log語句只接受了一個String參數(shù)缔俄,第二個Log接收的是一個Object數(shù)組弛秋,第三個Log接受一個String類型的分隔符(即{0})以及一個帶有分隔符的Object數(shù)組。

? ? ? ?那么要寫出這些方法對應(yīng)的接口就容易了:

interface ILogger
{
  void Log(string message);
  void ConcatLog(params object[] messageParts);
  void FormatLog(string format, params object[] parameters);
}

? ??請注意俐载,這個時候你不用在意他們到底是怎么實(shí)現(xiàn)的蟹略,你需要的只是一個能夠這樣完成方法調(diào)用的接口而已。如果你覺得還需要其他的Log執(zhí)行方法遏佣,很簡單挖炬,加到這個接口上就行了。這就是完全體現(xiàn)了“我只管調(diào)用方法状婶,我無需在乎具體實(shí)現(xiàn)”意敛。


抽象類--我能給你提供一個基礎(chǔ)的實(shí)現(xiàn)方案

? ? 在我看來,抽象類是在如下情形下才有價值:當(dāng)你發(fā)現(xiàn)一個接口中帶有一個“幾乎不變”而且會被之后的實(shí)現(xiàn)類“反復(fù)使用”的方法的時候膛虫,你便有必要把這個方法給具體實(shí)現(xiàn)出來草姻,而這樣的一個接口,也就進(jìn)而轉(zhuǎn)變成了一個抽象類走敌。

? ??繼續(xù)以前面的例子為例碴倾,如果將Log日志記錄到數(shù)據(jù)庫逗噩,或是文本或者甚至通過TCP/IP發(fā)送一封信件掉丽,那么我們可以假設(shè)ConcatLog()與FormatLog()方法會實(shí)現(xiàn)成這樣:

public void ConcatLog(params object[] messageParts)
{
  string message = string.Concat(messageParts);
  Log(message);
}
public void FormatLog(string format, params object[] parameters)
{
  string message = string.Format(format, parameters);
  Log(message);
}

? ??所以,可以建立一個抽象類來實(shí)現(xiàn)上面兩個方法异雁,但繼續(xù)保持Log()方法為抽象方法捶障。這樣一來,這個抽象類已經(jīng)實(shí)現(xiàn)了3個方法中的2個纲刀,當(dāng)進(jìn)一步開發(fā)FileLogger项炼,DatabaseLogger 和 TcpIpLogger 的時候,開發(fā)人員可以繼承這個抽象類進(jìn)行開發(fā)示绊,避免了重復(fù)的代碼锭部。


干嘛不一開始就使用抽象類?

? ??好了面褐,看到我的這個例子拌禾,我想很多人會問:“為什么不一開始就使用抽象類,免得還采用這么一個無用的接口展哭?”

? ??少年請淡定湃窍,誰敢保證說這個接口是“無用的”闻蛀?

? ??再打個比方,假定我之前的那些方法實(shí)現(xiàn)并不完全正確——它并沒有校驗(yàn)傳入?yún)?shù)的合法性您市,一旦ConcatLog()方法調(diào)用的參數(shù)是NULL觉痛,將會出現(xiàn)一個ArgumentNullException,同樣的問題也會出現(xiàn)在theFormatLog()方法上茵休。如果我們是這個代碼的原開發(fā)者薪棒,我們有權(quán)利來修正代碼,那自然好辦榕莺;但如果這個存在問題的抽象類是來自于一個已經(jīng)編譯好的庫盗尸,而你自己只是一個可憐巴巴的使用者,這時候你怎么辦帽撑?泼各?(全文最警醒之言 ——譯者注)

? ??如果一個類庫的開發(fā)者更多的使用接口,將抽象類分別單獨(dú)存在亏拉,這樣我們便可以方便的重新實(shí)現(xiàn)我們需要的方法扣蜻,比如可以加入我們想要的錯誤返回碼等等。

? ??另外一個例子——NullLogger會如何呢及塘?NullLogger莽使,其實(shí)就是一個不會做任何事情的Log工具。

? ??你可以通過抽象類來進(jìn)一步實(shí)現(xiàn)這個NullLogger笙僚,但是三個方法中有兩個方法是在完全浪費(fèi)時間:調(diào)用方法會免不了執(zhí)行格式化芳肌、拼接參數(shù)等操作,但是這些內(nèi)容是完全不會有任何顯示的——因?yàn)樗荖ullLogger肋层,本來就是空數(shù)據(jù)亿笤。所以NullLogger即使實(shí)現(xiàn)了所有的方法,其實(shí)都是毫無作為栋猖,浪費(fèi)時間净薛。像這樣一種“無為”的做法非常普遍,主要用來避免對NULL的檢查蒲拉,甚至目前都已經(jīng)產(chǎn)生了一種設(shè)計模式:Null Object Pattern (空對象模式)肃拜。

? ??最后,依然假定我們是這個代碼的開發(fā)者雌团,我們提供給其他程序員使用燃领,那么我們還很難知道其他程序員對于這些方法的統(tǒng)計需求到底是怎樣的。假如有人覺得這些Logger不能僅僅用來輸出日志锦援,還要同時統(tǒng)計每個log方法的調(diào)用次數(shù)猛蔽。如果我們一開始使用的是抽象類的設(shè)計方案,那么可以看到具體的實(shí)現(xiàn)中總是導(dǎo)向Log()方法雨涛,我們在進(jìn)一步的擴(kuò)展里枢舶,只能去寫代碼統(tǒng)計Log()方法的調(diào)用次數(shù)懦胞,而其實(shí)很顯然其他方法也肯定有被調(diào)用,但沒法統(tǒng)計了凉泄。如果換做是完全重新實(shí)現(xiàn)所有接口躏尉,我們就可以避免這種問題,因?yàn)槊總€方法的實(shí)現(xiàn)后众,此刻由我們自己做主胀糜。

? ??所以,當(dāng)我們自己作為一個組件/類庫的開發(fā)提供者的時候蒂誉,我們要盡可能的讓組件內(nèi)容完全正確教藻,并且要允許用戶能夠重新重寫任何他們需要的內(nèi)容——有的時候你也免不了犯錯,抑或他們確實(shí)有什么特殊的需求需要特殊處理右锨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末括堤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绍移,更是在濱河造成了極大的恐慌悄窃,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹂窖,死亡現(xiàn)場離奇詭異轧抗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞬测,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門横媚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人月趟,你說我怎么就攤上這事灯蝴。” “怎么了狮斗?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵绽乔,是天一觀的道長。 經(jīng)常有香客問我碳褒,道長,這世上最難降的妖魔是什么看疗? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任沙峻,我火速辦了婚禮,結(jié)果婚禮上两芳,老公的妹妹穿的比我還像新娘摔寨。我一直安慰自己,他們只是感情好怖辆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布是复。 她就那樣靜靜地躺著删顶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淑廊。 梳的紋絲不亂的頭發(fā)上逗余,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機(jī)與錄音季惩,去河邊找鬼录粱。 笑死,一個胖子當(dāng)著我的面吹牛画拾,可吹牛的內(nèi)容都是我干的啥繁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼青抛,長吁一口氣:“原來是場噩夢啊……” “哼旗闽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蜜另,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宪睹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚕钦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亭病,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年嘶居,在試婚紗的時候發(fā)現(xiàn)自己被綠了罪帖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡邮屁,死狀恐怖整袁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情佑吝,我是刑警寧澤坐昙,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站芋忿,受9級特大地震影響炸客,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜戈钢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一痹仙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧殉了,春花似錦开仰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恩溅。三九已至,卻和暖如春谓娃,著一層夾襖步出監(jiān)牢的瞬間脚乡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工傻粘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留每窖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓弦悉,卻偏偏與公主長得像窒典,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稽莉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,757評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理瀑志,服務(wù)發(fā)現(xiàn),斷路器污秆,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法劈猪,類相關(guān)的語法,內(nèi)部類的語法良拼,繼承相關(guān)的語法战得,異常的語法,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • 《將夜》 ——作者:陸陳蔚 將夜歸來的鴿群 紛紛棲落在對面樓頂 它們的辛勞我聽不見 翅膀鼓起的塵灰我嗆不...
    陸陳蔚閱讀 309評論 0 1
  • 就跟寫作文一樣庸推,先寫正文再寫標(biāo)題吧常侦。是這樣,我有一個一直聯(lián)系的男性朋友贬媒,不知道算不算男閨蜜那種聋亡。突然說,雙...
    是我桃子呀閱讀 197評論 0 0