什麼時候使用接口(抽象類)?

一、接口的做用概述

接口的做用簡單能夠歸納爲:c++

一、避免客戶端去實例化某個類程序員

二、向上轉型的使用——多態算法

三、擴展:Java中還能依靠接口實現多繼承數據庫

二、接口的引進

先看這樣一個場景:某個果園裏如今有兩種水果,一種是蘋果,一種是香蕉,有客戶想採摘園子裏的水果,要求用 get() 方法表示,代碼以下:編程

蘋果類app

public class Apple {
    public void get() {
        System.out.println("獲得蘋果");
    }
}

香蕉類編程語言

public class Banana {
    public void get() {
        System.out.println("獲得香蕉");
    }
}

客戶端工具

public static void one() {
        // 實例化一個apple
        Apple apple = new Apple();
        // 實例化一個banana
        Banana banana = new Banana();

        apple.get();
        banana.get();
}

蘋果和香蕉各自維持一個屬於本身的 get()方法,直接使用了 new 運算符進行實例化對象的操做,以後分別調用本身的 get()方法,中規中矩的實現過程,很好,咱們完成了客戶的任務。這時有了新的需求——須要用採摘到的水果作果汁,使用 doJuice(對應的水果) 方法表示,水果類的代碼不變,客戶端新加的其餘代碼以下:學習

    private static void doJuice(Apple apple) {
        apple.get();
        System.out.println("作成果汁");
    }

    private static void doJuice(Banana banana) {
        banana.get();
        System.out.println("作成果汁");
    }

客戶端優化

public static void oneA() {
        // 實例化一個apple
        Apple apple = new Apple();
        // 實例化一個banana
        Banana banana = new Banana();

        doJuice(apple);
        doJuice(banana);
    }

任務完成,如今果園又引進了新品種,有橘子,西瓜,荔枝,葡萄,哈密瓜,火龍果,梨……

仍是要採摘這些水果真後作果汁,若是仍是用以前的代碼實現,試想一下,除了必須加的新水果類以外,在客戶端裏還要爲每個水果類型分別添加對應的 doJuice(水果) 方法,然而水果種類那麼多……少一個就不行,怎麼改進呢?

爲了增長程序的靈活性,引進接口。類圖修改成:

作果汁的方法只須要一個便可,參數是接口類型:

    private static void doJuiceB(Fruit fruit) {
        fruit.get();
        System.out.println("作成果汁");
    }

客戶端

    private static void two() {
        // 使用接口的引用指向子類的對象,向上轉型過程,用到了多態
        Fruit apple = new AppleA();
        Fruit banana = new BananaA();
        Fruit orange = new OrangeA();

        doJuiceB(apple);
        doJuiceB(banana);
        doJuiceB(orange);
    }

事實上,想把各個水果都抽象化,能夠選擇抽象類或者接口去實現,而如今咱們要建立不帶任何方法定義和成員變量的抽象化的類,首選的應該是接口。

三、爲何要定義一個接口?

接口不只僅是簡單的抽象,它比對類型的抽象(抽象類)更進一步,是一種更純粹的抽象過程。由於接口沒有任何實現,沒有任何和接口相關聯的存儲(PS,Java 8 對接口已經支持能夠帶實現的 default 方法),所以也就沒法阻止多個接口的組合(多繼承)。

前面例子裏,抽象的其實更是對採集水果這個動做的抽象,用接口表示最好不過。客戶端裏使用了多態機制,能夠把任何一個水果類對象當參數傳入到 doJuice 方法裏,基於這個設計,使得 Java 程序員不用再爲相似的場景作出多餘的努力,這就是使用接口的核心緣由之一。由於這會使得程序變得很是靈活,並且經過繼承還能對接口進行擴展——舊的接口去 extends 新的接口。

使用接口的另外一個緣由是和抽象類相似——避免某個類被實例化,告訴編譯器和程序員,這個類不須要實例化,咱們只是爲了對某些行爲作出規範,你們想用就去遵照這個規則。基於此,有的編程語言(好比 iOS 開發的 Objective-C)已經不把接口叫 interface,直接叫 protocol。

統一標準的目的是讓你們都知道這個是作什麼,可是不用知道怎麼作,故這個類型不須要客戶端去實例化,這一點和抽象類是一致的,更通俗的說接口就是個招牌。

好比說看到這個圖標:

一看就知道這是 7-11 便利店,這個圖標就是接口,人們遠遠的看到了這個接口,就知道這個店是 7-11 便利店(實現接口)。固然,這個店也能夠不掛 7-11 的牌子,直接賣東西(直接寫實現方法),那樣咱們就不能一看見店鋪門,就簡單粗暴的知道這個店鋪是24小時便利商店……相反,須要採起以下作法:

一、沿着馬路邊走到每一個店鋪近前去觀察,哪一個是24小時營業的?(這就是反射的實現),很顯然這樣一家家的問實在是很是麻煩(通常狀況下,反射不如直接 new 來的直接)……

二、要麼咱們就記住,xxx路xxx號店鋪是24小時營業的便利店,離着它 200 米的 xxxx 號也是(這就是硬編碼實現),很顯然這樣咱們要記住的不少東西(代碼量劇增),並且若是有新的便利店開張,咱們也不可能知道(不利於擴展)……

而實現了接口,就意味着:店鋪門前掛了這個招牌,咱們不用進去問,甚至不用走到店鋪門前,遠遠看一眼,看到了這個標誌,就知道這個店鋪是 7-11 便利店,24小時營業的

再舉一個相似的例子,你們都知道吉祥餛飩。

接口也能夠比喻爲吉祥餛飩的連鎖招牌,每一個連鎖店都有同樣的餛飩菜單,同樣的餛飩作法,同樣的總部的餛飩配料……可是每一個店鋪實現的味道,每一個店鋪實現過程當中的衛生狀況……你們就不知道了,只能直觀的,或者從網上平臺去看各個店鋪的口碑(具體類對接口的不一樣實現)。

一樣對上一個例子—7-11便利店來講,人們不須要去具體詢問店員大家的店鋪是24小時營業的便利店麼?人們只須要,也只能瞭解到 7-11 這個牌子表明的意思就足夠了……映射到程序裏,就是對具體的類來講,每個方法是怎麼實現的,調用者不知道,其實也不在意。調用者它只須要知道接口的知識,也最多隻能知道接口的知識,所以這是一個很好的抽象過程,和把不一樣層次的工做內容快速分離的過程。

四、項目裏即便只有一個類使用接口,也不厭其煩的定義接口,且從業務上看,將來也不太可能有其餘類用這個接口,那定義這個接口意義在哪裏?

三個字:沒卵用。一個好的系統是一步步的重構,優化獲得的,而不是開始就設計出來的,避免早期的過分優化!

《Thinking in Java》一書說到「肯定接口是理想選擇,於是應該老是選擇接口而不是具體的類,這實際上是一個誘惑!由於對於建立類,幾乎任什麼時候候,均可以建立接口來代替!許多人都陷入了這個陷阱,在編寫代碼的時候,只要有那麼一絲可能就去肆無忌憚的建立接口……貌似是由於須要使用不一樣的具體實現,實際上已是過分優化,已經變成了一種草率的設計!任何抽象都應該以真正的需求出發。必要的時候,應該是重構接口而不是處處添加額外級別的間接性,並所以帶來額外的複雜性,恰當的原則應該是:優先想到使用類,從類開始,若是接口的設計需求變得很是明確了,好的,進行重構吧!記住,接口雖然是一個很重要的工具,可是極其容易被濫用!」

五、抽象類能代替接口麼?

有人說:我以爲抽象類徹底能夠替代接口,接口能作的抽象類均可以,並且抽象類還能包括實現。這比接口更強大……

到這裏,先打住這個提問,別忘了 Java 不支持多繼承!若是隻是使用抽象類則必須繼承 abstract class,而 Java 只容許單繼承,因此僅在這一點上,接口存在就已經十分有意義了,它解決了 Java 不容許多繼承的問題。

六、接口和抽象類有什麼區別,選擇使用接口和抽象類的依據是什麼?

接口和抽象類的概念不同。

接口是對動做的抽象,抽象類是對類型的抽象。

抽象類表示這個對象是什麼,接口表示這個對象能作什麼……

好比,人分男人,女人這兩個類,他們的抽象類是人,這種繼承關係說明他們都是人,而人均可以吃東西,然而狗也能夠吃東西,咱們就能夠把「吃東西」這個動做定義成一個接口,而後其餘須要的類去實現它,從而具有吃的行爲。

因此,在高級語言上合理的繼承設計就是:一個類只能繼承一個類(抽象類,正如人不可能同時是生物和非生物),可是能夠實現多個接口(吃飯、走路),這點是c++的缺陷。總結來講:

第一點. 接口是抽象類的更高一級的抽象,接口沒有任何相關自身的存儲,接口中全部的方法默認都是抽象的,默認都是public的,而抽象類只是聲明方法的存在而不去實現它的類,須要public方法,則須要程序員指定訪問權限

第二點. 接口能夠多繼承,抽象類不行

第三點. 接口定義方法,不能實現(Java8之後,接口也能夠有default實現方法了),而抽象類能夠實現部分方法,也就是抽象類能夠同時存在普通方法和抽象方法

第四點. 接口中基本數據類型默認爲 public static, 而抽類象不是的。

 一句話區分:當你關注一個事物的本質的時候,能夠抽象共同的部分,作爲抽象類;當你關注一個操做的時候,用接口。

七、接口在開發過程當中能夠快速分離工做內容

上層業務的開發者在寫顧客購買商品須要支付這個業務邏輯的時候須要一個功能,就是鏈接數據庫去訪問顧客的商城錢包,可是他的工做專一於實現業務邏輯,不想分開精力去作底層實現,那麼他只須要先定義一個接口,而後就能夠繼續他的業務邏輯代碼了,而底層業務(算法,數據庫鏈接,數據存儲等)實現者能夠根據這個接口,作他本身具體的實現,上層調用者不須要也不該該去知道底層實現,他只須要了解到接口這一級別便可。這樣經過使用接口就能夠快速的分離工做內容,達到團隊並行工做的目的。

此外,若是規範是經過接口定義的,那麼當你這個功能有多個實現時,你只要實現了這個接口,那麼能夠快速的替換具體實現,作到代碼層面的徹底分離。

總結起來就一句話:接口或者規範能夠在開發過程當中作到工做內容的分離。

團隊的 A 寫接口,B 寫實現,C 寫實現……B、C就不用寫接口,B、C 看到 A 的接口就知道我要具體去實現什麼功能,供上層調用者 A 使用便可,而對於 A 來講,不給大家統一規定好了,大家怎麼知道該如何去實現哪些具體內容……

好比一樣是登錄操做,A 不統一規定爲 login(xxx); 那麼頗有可能 C 寫一套登陸實現叫 loginA,B 寫一套登陸實現叫 denglu……具體到程序裏就會讓人困惑……且沒法快速的替換不一樣的實現過程。

更進一步,一個任務,A做爲上層調用者,它須要一個算法方面的功能,可是 A 不須要去具體學習相關算法和實現,因而 A 就去寫一個接口,而讓 B 這個底層開發人員寫實現,可是 B 偏偏今天不在公司,A 明天要出差,任務後天就交工,那 A 今天必須把使用這個接口的代碼寫好,A 寫好接口,明天 B 把接口實現傳個實例進來,任務 ok 交工。

故 interface 換個叫法就是 contract,有點合同的意思。B實現了這個接口,表明 B 承諾能作某些事情。A 須要一些能作某些事情的東西,因而 A 要求,必須實現了接口,才能被我調用。實際上也就是個「規範」。

相關文章
相關標籤/搜索