接口究竟是什麼?

學習Spring有一段時間了,對java也有了一點了解,最不能理解的就是接口, 即便是寫了接口並實現了它,依然沒法理解它到底有什麼用?看了其餘幾篇博客,總結了一下本身的理解。html

在JAVA編程語言中是一個抽象類型,是抽象方法的集合。一個類經過繼承接口的方式,從而來繼承接口的抽象方法。

先從一個通俗的解釋看起 (原文:C# 接口《通俗解釋》java


若是你的工做是一個修水管的,一天客戶找上你讓你幫裝水管,可是有個要求,就是客戶喜歡管子是三角形的。編程

你立馬買了三角形的水管回來,在牆上弄個三角形的口子,客戶付了錢,你很開心今天有了收入,以下圖,很好:編程語言

280101020477366.png

可是好景不長,客戶過了一個星期又來找,由於他以爲三角形很差看,要讓你換成正方形的水管,你不得不換,由於顧客就是上帝。好吧,繼續在牆上弄個正方形的口子,而後又換成正方形的管子來接上。好了,以下圖:(可是可能以爲爲何一開始不說要正方形的?由於需求總在變化。。。)學習

280104040472031.png

你累得滿頭大汗,可是仍是完成了。惋惜不久,客戶又來找你,由於他想換成橢圓形的管子了。雖然你很無奈,可是你仍是不得不花了幾個小時完成。以下圖:spa

280105478602182.png

安裝完成,這時你可能在考慮,爲何換不一樣形狀的水管,我都要大動干戈一番呢?因而你想到一個好方法,那就是牆上設計一個固定的水管而且是圓形的,當客戶喜歡什麼形狀的水管,那麼我只須要把客戶喜歡的水管的一頭作成圓形的,這樣,之後都不須要去動牆上的水管了。這是一個好辦法。就先在牆上弄個圓形的口,這個口就叫作接口。以下圖:設計

280109385166981.png

如你所見,牆上有個圓形的口,可是按照本來的:code

三角形水管兩端是三角形
正方形水管兩端是正方形
橢圓形水管兩端是橢圓形htm

那是確定接不上的,由於三角形、正方形、橢圓形的口怎麼和牆壁上圓形的口對接呢?對象

因此先要實現接口,把:

三角形水管一端作成圓形
正方形水管一端作成圓形
橢圓形水管一端作成圓形

如圖,因此,圓形接口作出來了,具體實現是客戶去安裝,接口自己並不會安裝其餘形狀的水管,換句話說就是接口沒有具體實現,只是告訴你,你的水管要接入,必須有一端是圓形的(接口的約束),由於我只留這個圓形的接口,你要裝別的形狀的管子,先把一個弄成圓形的就行了(子類去實現接口的方法),無論什麼形狀,都要一個必須作成圓形才能對接得上,它必需要你按照個人規範來作。這就是爲何新手會以爲接口什麼都不作,只定義接口,沒有任何實現,那不是畫蛇添足嗎?由於它的實現是子類去完成。這樣只要客戶喜歡什麼形狀的水管,只要實現了個人接口(圓形),都能對接得上,並且改變起來也很方便,只要把水管扭上去就好了,不用在去給牆壁挖洞了。


初步瞭解接口,再用代碼進一步理解

需求:公司有兩我的分別寫了2個動物類,讓你寫一個類來輸出它們各自喜歡的食物。

//A寫的Dog類,裏面有個likeFood方法
class Dog 
{

    public void likeFood()
    {
        Print("我是小狗,我喜歡吃肉");
    }
    
}
//B寫的Cat類,裏面有個likeFood方法,以下:
class Cat 
{

    public void likeFood()
    {
        Print("我是小貓,我喜歡吃魚");
    }
    
}

你寫的Zoo類以下:

//動物園類
class Zoo {

    public void show(Dog dog){
        dog.likeFood();
    }

    public void show(Cat cat){
        cat.likeFood();
    }

}

在輸出的時候使用以下:

void Main() 
{
    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
}

這一切工做良好,但好景不長,公司又須要給動物園增長一個猴子,讓C去寫這個Monkey類,並能輸出它喜歡的食物。

class Dog 
{

    public void likeFood()
    {
        Console.WriteLine("我是小狗,我喜歡吃肉");
    }
    
}
//B寫的Cat類,裏面有個likeFood方法,以下:
class Cat 
{

    public void likeFood()
    {
        Console.WriteLine("我是小貓,我喜歡吃魚");
    }
    
}
//C寫的Monkey類,裏面有個likeFood方法,以下:
class Monkey 
{

    public void likeFood()
    {
        Console.WriteLine("我是猴子,我喜歡吃桃子");
    }
    
}

因而你的Zoo類就變成了這樣,僅僅多了一個重載方法:

class Zoo 
{
    public void show(Dog dog)
    {
        dog.likeFood();
    }

    public void show(Cat cat)
    {
        cat.likeFood();
    }

    public void show(Monkey money)
    {
        money.likeFood();
    }
}

void Main() 
{

    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
    zoo.show(new Monkey()); //"我是猴子,我喜歡吃桃子"

}

雖然只是增長了一個類,但善於思考的你立刻就會想到:「若是後面還有更多動物要輸出它們喜歡的食物,個人Zoo類都要修改,這對我來講不是一件好事。」
仔細觀察Zoo類,發現不變的是show方法,變化的是show方法是參數。由於每一個動物都不同,因此參數也就不同。因此原來就須要重載多個方法。
若是有一個類,能接收全部動物,那不就解決了?沒錯,因而你想到了定義一個父類叫Animal,裏面有個likeFood方法,讓全部動物類去繼承Animal。

class Zoo 
{

    public void show(Animal animal)
    {
        animal.likeFood();
    }

}

class Animal 
{

    public void likeFood()
    {
        Print("我是Animal類");
    }

}

你告訴原來的A和B兩人,讓它們寫的動物類都去繼承Animal,而且裏面有個輸出動物喜歡食物的方法。

class Dog : Animal 
{

    public void likeFood()
    {
        Print("我是小狗,我喜歡吃肉");
    }
    
}

class Cat : Animal 
{

    public void likeFood()
    {
        Print("我是小貓,我喜歡吃魚");
    }
    
}

class Monkey : Animal 
{

    public void likeFood()
    {
        Print("我是猴子,我喜歡吃桃子");
    }
    
}

運行也一切良好,無論你之後還有什麼類,只要讓須要添加的動物類,繼承Animal,並有個likeFood方法,那麼你無需修改Zoo,只須要再main方法裏傳入動物類的實例就能正常工做。
你大讚你聰明絕頂,這樣一來,你的Zoo根本不須要改變了。
有一天,公司新來一我的,暫時叫D吧,公司讓D寫個兔子類,你告訴D,你寫的Rabbit類必須繼承Animal,而且有一個它喜歡的食物的方法。

class Rabbit : Animal 
{

    public void favoriteFood()
    {
        Print("我是兔子,我喜歡吃蘿蔔");
    }
    
}

void Main() 
{

    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
    zoo.show(new Monkey()); //"我是猴子,我喜歡吃桃子"
    zoo.show(new Rabbit()); //"我是Animal類"

}

Raabit並無輸出預想的結果,你不得不花了點時間去排查緣由,最後你發現這不是什麼大問題,由於新來的D雖然寫了Rabbit類,可是他並不知道大家以前約定的動物喜歡的食物命名爲:likeFood()
D寫的Rabbit類,裏面的方法叫:favoriteFood()
雖然最後D修改他的Rabbit類的方法爲likeFood(),但你仍是對這個問題作了一番思考,爲何會致使這個問題?
那是由於沒有一種約束,使得子類繼承父類的時候必須實現父類的方法。有沒有一個類,能讓它的子類必須實現它定義的方法?有,那就是接口
因而你修改Animal爲接口,代碼以下:

interface Animal {

    public void likeFood();
    
}

總結

由上述的例子能夠總結接口的幾個用處:

  1. 使代碼易於維護,無論增長多少動物類,只要實現了animal接口,就都能拿來用
  2. 制定一種標準和規範,減小命名不統一帶來的一系列問題
  3. 方便團隊開發,設計者不用關心具體方法的實現,實現者能夠清楚地知道本身的任務是什麼。

這裏還有一個問題:在我寫代碼時,都是先有方法,對象,再有類,而後纔去抽象成接口,那麼個人每個類都須要抽象成接口嗎?在開發過程當中,每寫一個方法都要去寫它的接口的抽象方法,實現它的serviceTest以及調用它的controllerTest,最後才能真正放到程序中去用,即便是一個小小的功能都要作不少「無用功」,並且需求一旦變動,改起來不是更麻煩?

相關文章
相關標籤/搜索