學習Spring有一段時間了,對java也有了一點瞭解,最不能理解的就是介面, 即使是寫了介面並實現了它,依然無法理解它到底有什麼用?看了其他幾篇博客,總結了一下自己的理解。 在JAVA編程語言中是一個抽象類型,是抽象方法的集合。一個類通過繼承介面的方式,從而來繼承介面的抽象方法。 先從一個通俗的解 ...
學習Spring有一段時間了,對java也有了一點瞭解,最不能理解的就是介面, 即使是寫了介面並實現了它,依然無法理解它到底有什麼用?看了其他幾篇博客,總結了一下自己的理解。
在JAVA編程語言中是一個抽象類型,是抽象方法的集合。一個類通過繼承介面的方式,從而來繼承介面的抽象方法。
先從一個通俗的解釋看起 (原文:C# 介面《通俗解釋》)
如果你的工作是一個修水管的,一天客戶找上你讓你幫裝水管,但是有個要求,就是客戶喜歡管子是三角形的。
你立馬買了三角形的水管回來,在牆上弄個三角形的口子,客戶付了錢,你很開心今天有了收入,如下圖,很好:
但是好景不長,客戶過了一個星期又來找,因為他覺得三角形不好看,要讓你換成正方形的水管,你不得不換,因為顧客就是上帝。好吧,繼續在牆上弄個正方形的口子,然後又換成正方形的管子來接上。好了,如下圖:(但是可能覺得為什麼一開始不說要正方形的?因為需求總在變化。。。)
你累得滿頭大汗,但是還是完成了。可惜不久,客戶又來找你,因為他想換成橢圓形的管子了。雖然你很無奈,但是你還是不得不花了幾個小時完成。如下圖:
安裝完成,這時你可能在考慮,為什麼換不同形狀的水管,我都要大動干戈一番呢?於是你想到一個好方法,那就是牆上設計一個固定的水管並且是圓形的,當客戶喜歡什麼形狀的水管,那麼我只需要把客戶喜歡的水管的一頭做成圓形的,這樣,以後都不需要去動牆上的水管了。這是一個好辦法。就先在牆上弄個圓形的口,這個口就叫做介面。如下圖:
如你所見,牆上有個圓形的口,但是按照原本的:
三角形水管兩端是三角形
正方形水管兩端是正方形
橢圓形水管兩端是橢圓形
那是肯定接不上的,因為三角形、正方形、橢圓形的口怎麼和牆壁上圓形的口對接呢?
所以先要實現介面,把:
三角形水管一端做成圓形
正方形水管一端做成圓形
橢圓形水管一端做成圓形
如圖,所以,圓形介面做出來了,具體實現是客戶去安裝,介面本身並不會安裝其他形狀的水管,換句話說就是介面沒有具體實現,只是告訴你,你的水管要接入,必須有一端是圓形的(介面的約束),因為我只留這個圓形的介面,你要裝別的形狀的管子,先把一個弄成圓形的就好了(子類去實現介面的方法),不管什麼形狀,都要一個必須做成圓形才能對接得上,它必須要你按照我的規範來做。這就是為什麼新手會覺得介面什麼都不做,只定義介面,沒有任何實現,那不是多此一舉嗎?因為它的實現是子類去完成。這樣只要客戶喜歡什麼形狀的水管,只要實現了我的介面(圓形),都能對接得上,而且改變起來也很方便,只要把水管扭上去就行了,不用在去給牆壁挖洞了。
初步瞭解介面,再用代碼進一步理解
需求:公司有兩個人分別寫了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();
}
總結
由上述的例子可以總結介面的幾個用處:
- 使代碼易於維護,不管增加多少動物類,只要實現了animal介面,就都能拿來用
- 制定一種標準和規範,減少命名不統一帶來的一系列問題
- 方便團隊開發,設計者不用關心具體方法的實現,實現者可以清楚地知道自己的任務是什麼。
這裡還有一個問題:在我寫代碼時,都是先有方法,對象,再有類,然後才去抽象成介面,那麼我的每一個類都需要抽象成介面嗎?在開發過程中,每寫一個方法都要去寫它的介面的抽象方法,實現它的serviceTest以及調用它的controllerTest,最後才能真正放到程式中去用,即使是一個小小的功能都要做很多“無用功”,而且需求一旦變更,改起來不是更麻煩?