2009-04-08 29 views
3

我有三個接口,祖父母,父母和孩子的層次結構。 父母和孩子有一個方法「添加」,這需要在孩子中有不同的輸入參數。雖然在孩子中添加所需的簽名沒有問題,但繼承的方法將是毫無意義的,那麼有沒有辦法讓它在那裏呢? 其他方法正常工作。在Java界面中,我如何不*使用從父界面繼承的特定方法?

也許,以達到我想要的,我可以完全改進設計,所以我很快就會勾勒出接口是什麼:

我收集電錶讀數包括一個時間和一個值。祖父母界面是一次閱讀。我也有一些代表連續讀數(一系列)的類,以及包含在同一時間段內運行的多個系列的類(我們稱之爲表)。

該表可以被看作是一個系列(它與時間軸正交地聚合值),並且表和系列可以被看作是單個讀取(提供不同聚合方式的實現),因此是繼承。這似乎工作得很好,但對於添加方法。 (我可以在該系列中添加單點,但對於表格,我需要一個附加參數來告訴我它屬於哪個系列。)

回答

6

也許將所有接口繼承全部打破是有意義的。只需具有特定類型行爲的特定界面即可。無論實現這些接口的任何類都可以選擇那些有意義的類,也不必擔心實現沒有意義的方法。

0

接口是合同。這意味着任何實現該接口的東西都必須實現所定義的方法。從技術上講,你可以把它作爲一個虛擬方法來實現(沒有主體,只是返回,不管),但就我所知,它必須被實現。

9

不,你不能避免繼承的方法,因爲這樣做會違反Liskov substitution principle

在實踐中,你可以讓實現拋出一個UnsupportedOperationException,但這將是非常討厭的。

你不能執行帶有某種默認值的該系列繼承的方法?

+0

+1爲明確的答案爲什麼這不是接口如何工作。我同意對試圖按照合同行事的人拋出例外是令人討厭的。我會研究Andy White對於解決方案的建議。 – 2009-04-08 20:54:43

0

您可以隨時實現方法,空,如:

class A implements B{ void add(A) { /*Goes Nowhere Does Nothing*/ return;} } 

不過說真的,這不是一個好主意。一個更好的解決方案是,所有的祖父母,父母和孩子都會有兩個額外的方法 - hasParent():boolean和hasChild():boolean。這有利於作爲一個liskov替代兼容的變化以及更清潔的設計。

4

與繼承的問題是,將重點放在語言機制,使人們對實現,而不是語義。

當B從A繼承時,它意味着B的每個實例也是A的一個實例。在OOP中,作爲某個事物的實例通常意味着您應該對其方法有一個明智的響應,並且至少支持它們的消息。

如果你覺得是b不應該支持的消息之一,那麼就我而言,你有兩種選擇:

BAD - 拋出一個「未使用」例外,你會與合集得到框架。但是,這在我看來是糟糕的形式。

良好 - 接受B不是A的一種類型,避免繼承或重構它(例如,使用組合和/或接口),以便您不必重寫代碼,但不使用子類型關係。如果您的應用程序會隨着時間的推移而生存,那麼您不希望在層次結構中出現語義問題。

+0

+1。據我瞭解你的問題,Hanno,最好用組合而不是繼承。更多詳細信息,請訪問:http://c2.com/cgi/wiki?UseCompositionAndInterfacesWithoutClassInheritance – Olivier 2009-04-08 21:19:55

1

感謝您將我放在正確的軌道上,我upvoted我發現最有用的帖子。由於我的解決方案受到帖子的啓發,但未發佈,我將分享我的決定:

由於層次結構的靈感來自數據應該如何查看,而問題出現在語義上你如何加入數據,我打算將系列和表的接口分成讀和寫接口。寫入接口與彼此無關,讀取接口可以繼承而不會發生衝突。

我會做這個wiki,以防有人想要擴展這個。

1

你可能想看看Refused Bequest的代碼味道。

+0

+1,該網站對於一些經典場景似乎非常有用。 – 2009-04-08 21:46:17