何時應該使用接口,何時應該使用基類? 算法
若是我不想實際定義方法的基本實現,是否應該始終是一個接口? 編程
若是我有貓狗班。 爲何我要實現IPet而不是PetBase? 我能夠理解具備用於ISheds或IBarks(IMakesNoise?)的接口,由於能夠將它們逐個放置在每一個寵物上,可是我不知道用於普通Pet的接口。 框架
我有一個粗略的經驗法則 編碼
功能:各個部分可能有所不一樣:接口。 spa
數據和功能,部分將大部分相同,而部分則不一樣:抽象類。 插件
數據和功能,若是僅進行了少許更改就能夠實際使用:普通(具體)類 設計
數據和功能,沒有計劃的更改:帶有final修飾符的普通(具體)類。 指針
數據,可能還有功能:只讀:枚舉成員。 code
這是很是粗糙且準備就緒的,而且根本沒有嚴格定義,可是從接口的頻譜能夠將全部內容更改成枚舉,其中將全部內容都固定爲只讀文件便可。 對象
這是接口和基類的基本和簡單定義:
乾杯
在Java World這篇文章中很好地解釋了
我我的傾向於使用接口來定義接口,即系統設計中指定應如何訪問內容的部分。
我會有一個實現1個或多個接口的類並很多見。
我將抽象類用做其餘內容的基礎。
如下是上述文章JavaWorld.com文章的做者Tony Sintes的摘錄,04/20/01
接口與抽象類
選擇接口和抽象類不是一個選擇。 若是須要更改設計,請使其成爲界面。 可是,您可能具備提供某些默認行爲的抽象類。 抽象類是應用程序框架內的優秀候選者。
抽象類使您能夠定義一些行爲。 他們強迫您的子類提供其餘人。 例如,若是您有一個應用程序框架,則抽象類能夠提供默認服務,例如事件和消息處理。 這些服務容許您的應用程序插入您的應用程序框架。 可是,只有您的應用程序才能執行某些特定於應用程序的功能。 此類功能可能包括啓動和關閉任務,這些任務一般取決於應用程序。 所以,抽象基類能夠聲明抽象的關閉和啓動方法,而沒必要嘗試定義該行爲自己。 基類知道它須要那些方法,可是抽象類讓您的類認可它不知道如何執行這些動做。 它只知道它必須啓動動做。 是時候啓動了,抽象類能夠調用啓動方法。 當基類調用此方法時,Java會調用子類定義的方法。
許多開發人員忘記了定義抽象方法的類也能夠調用該方法。 抽象類是建立計劃的繼承層次結構的絕佳方法。 對於類層次結構中的非葉子類來講,它們也是一個不錯的選擇。
類與接口
有人說您應該根據接口定義全部類,可是我認爲建議彷佛有些極端。 當我發現設計中的某些東西會常常變化時,我會使用接口。
例如,使用策略模式,您能夠將新算法和過程交換到程序中,而無需更改使用它們的對象。 媒體播放器可能知道如何播放CD,MP3和WAV文件。 固然,您不想將這些播放算法硬編碼到播放器中; 這將使添加AVI等新格式變得困難。 此外,您的代碼中會堆滿無用的case語句。 爲了增長侮辱性傷害,您每次添加新算法時都須要更新這些案例陳述。 總而言之,這不是一種很是面向對象的編程方式。
使用策略模式,您能夠簡單地將算法封裝在對象後面。 若是這樣作,則能夠隨時提供新的媒體插件。 咱們將其稱爲插件類MediaStrategy。 該對象將具備一個方法:playStream(Stream s)。 所以,要添加新算法,咱們只需擴展算法類便可。 如今,當程序遇到新的媒體類型時,它只是將流的播放委託給咱們的媒體策略。 固然,您將須要一些管道來正確地實例化所需的算法策略。
這是使用界面的絕佳場所。 咱們使用了策略模式,該模式清楚地代表了設計中將會改變的位置。 所以,您應該將策略定義爲接口。 當您但願對象具備某種類型時,一般應該優先考慮接口而不是繼承。 在這種狀況下是MediaStrategy。 依靠繼承得到類型標識是危險的; 它將您鎖定在特定的繼承層次結構中。 Java不容許多重繼承,所以您不能擴展某些能夠提供有用的實現或更多類型標識的東西。
經過def,接口提供了與其餘代碼進行通訊的層。 默認狀況下,類的全部公共屬性和方法都實現隱式接口。 咱們還能夠將接口定義爲一種角色,當任何類都須要扮演該角色時,它必須實現該接口,並根據實現該接口的類爲它提供不一樣的實現形式。 所以,當您談論接口時,您在談論多態性;當您談論基類時,您在談論繼承。 哎呀的兩個概念!
要記住的另外一種選擇是使用「具備」關係,也就是「根據」或「組成」實現。 有時候,與使用「是」繼承相比,這是一種結構更清晰,更靈活的方法。
從邏輯上講,「狗」和「貓」都「擁有」一個寵物可能沒有邏輯上的意義,但它避免了常見的多重繼承陷阱:
public class Pet { void Bathe(); void Train(Trick t); } public class Dog { private Pet pet; public void Bathe() { pet.Bathe(); } public void Train(Trick t) { pet.Train(t); } } public class Cat { private Pet pet; public void Bathe() { pet.Bathe(); } public void Train(Trick t) { pet.Train(t); } }
是的,此示例代表以這種方式執行代碼涉及不少代碼重複而且缺少優雅。 可是,還應該意識到,這有助於使Dog and Cat與Pet類脫鉤(由於Dog and Cat沒法訪問Pet的私有成員),而且爲Dog and Cat繼承從其餘事物繼承的空間- -多是哺乳動物類。
當不須要私人訪問而且您不須要使用通用Pet引用/指針來引用Dog和Cat時,最好使用組合。 接口爲您提供了通用的參考功能,能夠幫助減小代碼的冗長性,可是當它們組織得很差時,它們也可使事情變得模糊。 當您須要私有成員訪問時,繼承頗有用,而且在使用繼承時,您將致力於將Dog和Cat類與Pet類高度結合,這是一筆高昂的費用。
在繼承,組合和接口之間,沒有一種永遠正確的方法,它有助於考慮如何將全部三個選項和諧地使用。 在這三者中,繼承一般是應該最少使用的選項。