理論五:接口vs抽象類的區(qū)別鳄梅?如何用普通的類模擬抽象類和接口叠国?

在面向?qū)ο缶幊讨校橄箢惡徒涌谑莾蓚€經(jīng)常被用到的語法概念戴尸,是面向?qū)ο笏拇筇匦运诤福约昂芏嘣O(shè)計模式、設(shè)計思想孙蒙、設(shè)計原則編程實現(xiàn)的基礎(chǔ)项棠。比如,我們可以使用接口來實現(xiàn)面向?qū)ο蟮某橄筇匦钥媛汀⒍鄳B(tài)特性和基于接口而非實現(xiàn)的設(shè)計原則香追,使用抽象類來實現(xiàn)面向?qū)ο蟮睦^承特性和模板設(shè)計模式等等。

什么是抽象類和接口坦胶?區(qū)別在哪里透典?
首先,我們來看一下顿苇,在 Java 這種編程語言中峭咒,我們是如何定義抽象類的。
下面這段代碼是一個比較典型的抽象類的使用場景(模板設(shè)計模式)纪岁。Logger 是一個記錄日志的抽象類凑队,F(xiàn)ileLogger 和 MessageQueueLogger 繼承 Logger,分別實現(xiàn)兩種不同的日志記錄方式:記錄日志到文件中和記錄日志到消息隊列中幔翰。FileLogger 和 MessageQueueLogger 兩個子類復用了父類 Logger 中的 name漩氨、enabled西壮、minPermittedLevel 屬性和 log() 方法,但因為這兩個子類寫日志的方式不同叫惊,它們又各自重寫了父類中的 doLog() 方法款青。

// 抽象類
public abstract class Logger {
private String name;
private boolean enabled;
private Level minPermittedLevel;

public Logger(String name, boolean enabled, Level minPermittedLevel) {
this.name = name;
this.enabled = enabled;
this.minPermittedLevel = minPermittedLevel;
}

public void log(Level level, String message) {
boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValue());
if (!loggable) return;
doLog(level, message);
}

protected abstract void doLog(Level level, String message);
}
// 抽象類的子類:輸出日志到文件
public class FileLogger extends Logger {
private Writer fileWriter;

public FileLogger(String name, boolean enabled,
Level minPermittedLevel, String filepath) {
super(name, enabled, minPermittedLevel);
this.fileWriter = new FileWriter(filepath);
}

@Override
public void doLog(Level level, String mesage) {
// 格式化level和message,輸出到日志文件
fileWriter.write(...);
}
}
// 抽象類的子類: 輸出日志到消息中間件(比如kafka)
public class MessageQueueLogger extends Logger {
private MessageQueueClient msgQueueClient;

public MessageQueueLogger(String name, boolean enabled,
Level minPermittedLevel, MessageQueueClient msgQueueClient) {
super(name, enabled, minPermittedLevel);
this.msgQueueClient = msgQueueClient;
}

@Override
protected void doLog(Level level, String mesage) {
// 格式化level和message,輸出到消息中間件
msgQueueClient.send(...);
}
}

通過上面的這個例子,我們來看一下赋访,抽象類具有哪些特性可都。我總結(jié)了下面三點:
抽象類不允許被實例化,只能被繼承蚓耽。也就是說渠牲,你不能 new 一個抽象類的對象出來(Logger logger = new Logger(…); 會報編譯錯誤)。
抽象類可以包含屬性和方法步悠。方法既可以包含代碼實現(xiàn)(比如 Logger 中的 log() 方法)签杈,也可以不包含代碼實現(xiàn)(比如 Logger 中的 doLog() 方法)。不包含代碼實現(xiàn)的方法叫作抽象方法鼎兽。
子類繼承抽象類答姥,必須實現(xiàn)抽象類中的所有抽象方法。對應(yīng)到例子代碼中就是谚咬,所有繼承 Logger 抽象類的子類鹦付,都必須重寫 doLog() 方法

現(xiàn)在我們再來看一下,在 Java 這種編程語言中择卦,我們?nèi)绾味x接口敲长。

// 接口
public interface Filter {
void doFilter(RpcRequest req) throws RpcException;
}
// 接口實現(xiàn)類:鑒權(quán)過濾器
public class AuthencationFilter implements Filter {
@Override
public void doFilter(RpcRequest req) throws RpcException {
//...鑒權(quán)邏輯..
}
}
// 接口實現(xiàn)類:限流過濾器
public class RateLimitFilter implements Filter {
@Override
public void doFilter(RpcRequest req) throws RpcException {
//...限流邏輯...
}
}
// 過濾器使用demo
public class Application {
// filters.add(new AuthencationFilter());
// filters.add(new RateLimitFilter());
private List<Filter> filters = new ArrayList<>();

public void handleRpcRequest(RpcRequest req) {
try {
for (Filter filter : fitlers) {
filter.doFilter(req);
}
} catch(RpcException e) {
// ...處理過濾結(jié)果...
}
// ...省略其他處理邏輯...
}
}

上面這段代碼是一個比較典型的接口的使用場景。我們通過 Java 中的 interface 關(guān)鍵字定義了一個 Filter 接口秉继。AuthencationFilter 和 RateLimitFilter 是接口的兩個實現(xiàn)類祈噪,分別實現(xiàn)了對 RPC 請求鑒權(quán)和限流的過濾功能。
代碼非常簡潔尚辑。結(jié)合代碼辑鲤,我們再來看一下,接口都有哪些特性杠茬≡氯欤總結(jié)了三點:
接口不能包含屬性(也就是成員變量)。
接口只能聲明方法瓢喉,方法不能包含代碼實現(xiàn)吓坚。
類實現(xiàn)接口的時候,必須實現(xiàn)接口中聲明的所有方法灯荧。

抽象類和接口能解決什么編程問題?
首先盐杂,我們來看一下逗载,我們?yōu)槭裁葱枰橄箢惗吡克軌蚪鉀Q什么編程問題?
抽象類不能實例化厉斟,只能被繼承挚躯。而前面的章節(jié)中,我們還講到擦秽,繼承能解決代碼復用的問題码荔。所以,抽象類也是為代碼復用而生的感挥。多個子類可以繼承抽象類中定義的屬性和方法缩搅,避免在子類中,重復編寫相同的代碼触幼。

我們再來看一下硼瓣,我們?yōu)槭裁葱枰涌冢克軌蚪鉀Q什么編程問題置谦?
抽象類更多的是為了代碼復用堂鲤,而接口就更側(cè)重于解耦。接口是對行為的一種抽象媒峡,相當于一組協(xié)議或者契約瘟栖,你可以聯(lián)想類比一下 API 接口。調(diào)用者只需要關(guān)注抽象的接口谅阿,不需要了解具體的實現(xiàn)半哟,具體的實現(xiàn)代碼對調(diào)用者透明。接口實現(xiàn)了約定和實現(xiàn)相分離奔穿,可以降低代碼間的耦合性镜沽,提高代碼的可擴展性。

如何決定該用抽象類還是接口贱田?
實際上缅茉,判斷的標準很簡單。如果我們要表示一種 is-a 的關(guān)系男摧,并且是為了解決代碼復用的問題蔬墩,我們就用抽象類;如果我們要表示一種 has-a 關(guān)系耗拓,并且是為了解決抽象而非代碼復用的問題拇颅,那我們就可以使用接口。

重點:

  1. 抽象類和接口的語法特性抽象類不允許被實例化乔询,只能被繼承樟插。它可以包含屬性和方法。方法既可以包含代碼實現(xiàn),也可以不包含代碼實現(xiàn)黄锤。不包含代碼實現(xiàn)的方法叫作抽象方法搪缨。子類繼承抽象類,必須實現(xiàn)抽象類中的所有抽象方法鸵熟。接口不能包含屬性副编,只能聲明方法,方法不能包含代碼實現(xiàn)流强。類實現(xiàn)接口的時候痹届,必須實現(xiàn)接口中聲明的所有方法。
  2. 抽象類和接口存在的意義抽象類是對成員變量和方法的抽象打月,是一種 is-a 關(guān)系队腐,是為了解決代碼復用問題。接口僅僅是對方法的抽象僵控,是一種 has-a 關(guān)系香到,表示具有某一組行為特性,是為了解決解耦問題报破,隔離接口和具體的實現(xiàn)悠就,提高代碼的擴展性。
  3. 抽象類和接口的應(yīng)用場景區(qū)別什么時候該用抽象類充易?什么時候該用接口梗脾?實際上,判斷的標準很簡單盹靴。如果要表示一種 is-a 的關(guān)系炸茧,并且是為了解決代碼復用問題,我們就用抽象類稿静;如果要表示一種 has-a 關(guān)系梭冠,并且是為了解決抽象而非代碼復用問題,那我們就用接口改备。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末控漠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子悬钳,更是在濱河造成了極大的恐慌,老刑警劉巖默勾,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碉渡,死亡現(xiàn)場離奇詭異,居然都是意外死亡母剥,警方通過查閱死者的電腦和手機滞诺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門形导,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铭段,你說我怎么就攤上這事骤宣。” “怎么了序愚?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長等限。 經(jīng)常有香客問我爸吮,道長,這世上最難降的妖魔是什么望门? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任形娇,我火速辦了婚禮,結(jié)果婚禮上筹误,老公的妹妹穿的比我還像新娘桐早。我一直安慰自己,他們只是感情好厨剪,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布哄酝。 她就那樣靜靜地躺著,像睡著了一般祷膳。 火紅的嫁衣襯著肌膚如雪陶衅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天直晨,我揣著相機與錄音搀军,去河邊找鬼。 笑死勇皇,一個胖子當著我的面吹牛罩句,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播敛摘,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼门烂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了着撩?” 一聲冷哼從身側(cè)響起诅福,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拖叙,沒想到半個月后氓润,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡薯鳍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年咖气,在試婚紗的時候發(fā)現(xiàn)自己被綠了挨措。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡崩溪,死狀恐怖浅役,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伶唯,我是刑警寧澤觉既,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站乳幸,受9級特大地震影響瞪讼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粹断,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一符欠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓶埋,春花似錦希柿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闽颇,卻和暖如春盾戴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兵多。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工尖啡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剩膘。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓衅斩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親怠褐。 傳聞我的和親對象是個殘疾皇子畏梆,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

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