我以前對於java接口的理解,一直是覺得這個東西沒有什么太大用處,不如抽象類,可有可無的一個東西。但一個東西既然存在一定有他的意義,今天我就看到了接口的一個重要用法。
首先毋庸置疑的是,java單繼承的情況下,你只能繼承一個類,但可以實現多個接口,這也是我一直以為的接口唯一的用法:在單繼承不夠用的情況下實現其他的接口。
然后是我現在才知道的,接口里聲明的所有方法,在實現接口時都必須全部被實現——這可以強制實現該接口的類具有某一些特性。
這里舉一個例子說明:例子來自spring實戰這本書。
我們假設有一個繼承Knight接口的類:
package Knight_Quest; public class DamselRescuingKnight implements Knight{ private RescueDamselQuest quest; public DamselRescuingKnight(){ this.quest = new RescueDamselQuest(); } @Override public void embarkOnQuest() { quest.embark(); } }
這個類便有一個問題,首先該騎士與這種類型的任務綁死在了一起,也就是說這個類作為一個類復用性非常差(甚至想到於一個對象)。
由於在構造方法直接自行創建了RescueDamselQuest()對象,所以相當於該Knight只能執行這一種任務——如果一個少女需要救援,那么這個Knight就能招之而來,但是如果有惡龍需要殺掉,有圓桌會議要開,那么這位Knight也就愛莫能助了(僅僅是因為在構造方法中直接創建了某一種特定的對象,使得兩個類緊密的耦合在一起,無法分開)。更糟糕的是,在測試的時候,又會有一個新的問題——你無法保證在調用Knight類的embarkOnQuest()方法的時候,其下面的quest.embark()方法也能被正確的調用。所以這個類可以說是無法進行有效的測試。
所以我們就要使用依賴注入的思想來編寫:
我們可以創建一個Quest接口,且強制實現該接口的類(各種quest)都必須實現embark方法(這個embark方法可以根據類的不同而不同)
public interface Quest { public void embark(); } public class RescueDamselQuest implements Quest{ @Override public void embark() { } }
然后我們對BraveKnight進行改寫:
public class BraveKnight implements Knight{ private Quest quest; public BraveKnight(Quest quest){ this.quest = quest; } @Override public void embarkOnQuest() { quest.embark(); } }
這樣改造過的Knight就不同了,構造方法中傳入的是Quest這一接口,這樣一來,BraveKnight能夠響應DamselRescuingQuest、DragonSlayQuet、RoundtableQuest等各種Quest接口的實現類。這時構造Knight,就不是由Knight自行創建某個對象,而是將各種quest對象作為構造器的參數傳入——這就是依賴注入的方式之一。
這里的要點是 BraveKnight 沒有與任何特定的 Quest 實現發生耦合。對它來說,被要求挑戰的任務只要實現了 Quest 接口,那么具體是哪種類型的任務就無關緊要了。 這就是 DI(依賴注入) 所帶來的最大收益——松耦合。
測試時只需要使用mock就行。