2009-11-06 79 views
45

我似乎無法找到答案,只是想確保它是一個好的編碼標準。我有許多不同類使用的接口A,並且不希望接口A發生變化。我遇到了一個新的需求,需要實現接口A的許多類需要枚舉,但並不是所有的類都需要這個枚舉。我不希望那些不需要這個新枚舉的類實現這個新的功能。所以我創建了包含我需要添加的新枚舉的接口B.然後我做了Interface B繼承接口A,這是我的擔心,一個接口繼承另一個接口可以嗎?爲了繼續我的更改,我然後將需要新枚舉的類更改爲實現接口B而不是接口A,因爲它是由接口B繼承的。我想過在我的類中實現兩個接口,但需要它們,但我使用在整個代碼中的接口,並希望只使用一個接口來查看類而不是兩個。如果一個接口繼承另一個接口

我希望這已經夠清楚了(可能會很長),但是如果任何人都可以給我一些建議,或者我做得對或者我做錯了,請告訴我。

謝謝!

回答

60

接口繼承是一個很好的工具,但只有在接口B真正可以替代接口A時才應該使用接口繼承,而不僅僅是聚合鬆散相關的行爲。

很難判斷它是否適合您的具體情況,但原則上這種做法沒有錯。您始終可以在一流的API中看到它。從.NET框架中挑選一個常見示例:

public interface ICollection<T> : IEnumerable<T>, IEnumerable 
+2

我最初建議Liskov替代原則應該適用。回想起來,這並不是真的有意義。 LSP的大部分要求(保持不變量,對條件前後的限制)實際上只適用於具體的實現,而不適用於接口。話雖如此,可替代性的一般原則仍應指導接口繼承決策。 –

+0

Liskov替代原理:確保接口B可以完全替代接口A是非常重要的。否則,您將最終得到您不需要實現的功能。這會導致您不需要的額外代碼,從而導致軟件不穩定。 –

2

IMO這是完全正確的方法,我沒有看到任何問題。

6

當然可以有一個接口的繼承樹,甚至有接口的「多繼承」。無論是否正確,取決於所討論的接口。如果接口B確實是接口A的擴展或改進,那麼繼承是有意義的,但是如果新的枚舉與接口A所表達的概念很大程度上無關,那麼我會讓它們成爲兩個單獨的接口並且具有類需要實現這兩個接口。

+0

好點,是的,在這種情況下,他們是相關的。 – ajrawson

5

從技術上講,接口不會相互繼承。當你創建一個從IBar繼承的IFoo真的會發生什麼,你說任何實現IFoo的類都必須實現IBar

interface IBar 
{ 
    void DoBar(); 
} 

interface IFoo : IBar 
{ 
    void DoFoo(); 
} 

在這個例子中,IFoo接口不具有DoBar()方法。大多數情況下,區分並不重要,但在界面上而不是類中使用反射時,它會咬你。

+3

即使考慮到你描述的行爲(Phil Haack最近的專欄中詳細描述,http://haacked.com/archive/2009/11/10/interface-inheritance-esoterica.aspx),我認爲這是過分混淆當C#參考文檔甚至使用該術語時,在C#中說'接口不會彼此繼承',如「接口可以從一個或多個基接口繼承」。 (http://msdn.microsoft.com/en-us/library/87d83y5b%28VS.80%29.aspx)我只想說,在C#中,反射繼承的成員時,接口繼承的行爲與類繼承的行爲不同。 –

+1

夠公平的。我想這取決於如何定義「繼承」。當然,無論是類還是接口,C#語法都是一樣的。但是,如果將繼承看作是包含父項的成員,那麼當您談論接口時,您的心智模型與現實不符。 –

1

我認爲數據庫總是提供一個偉大的方式來演示接口,因此考慮如果接口應該繼承另一個接口看看下面,

IMySqlDatabase : IDatabase 
MySqlDatabase : IMySqlDatabase 

IMsSqlDatabase : IDatabase 
MsSqlDatabase : IMsSqlDatabase 

一個MySqlDatabase是IMySqlDatabase和IMySqlDatabase是了IDatabase。

現在,如果您需要對您的IDatabase接口進行更改,它的孫輩(conrete數據庫類)可以從中獲益,但您不必擴展MySQL和MsSQL(或者甚至更多的DBMS接口)。同時在你的中間人(IMsSqlDatabase)中,你仍然可以擁有MySQL或Oracle DB不支持的界面功能。

13

考慮接口是否應該在邏輯上配對,並且如果你覺得它們彼此配合良好,那麼絕對使用繼承。

讓我們看一個例子;

public interface IScanner 
{ 
    void Scan(); 
} 

public interface IPrinter 
{ 
    void Print(); 
} 

打印機和掃描儀往往是單獨的對象,每一個都有自己的功能但是這兩個設備都在同一設備中常常是成對;

public interface IPhotocopier : IScanner, IPrinter 
{ 
    void Copy(); 
} 

這是有道理的IPhotocopier應從所以IScanner和IPrinter繼承這是因爲現在允許複印機用作除了其主輥作爲複印機或者掃描儀或打印機(它包含)。

現在讓我們看看更多的接口;

public interface IBlender 
{ 
    void Blend(); 
} 

這將沒有任何意義,讓IBlender受到任何的早期接口的繼承(你會怎麼稱呼他們?IBlendingScanner?)。

如果你不能給你的新界面一個明智的名字,這可能表明你可能不希望在這種情況下使用繼承。

繼承一些接口(如IDisposable)是一個壞主意,因爲這會迫使您的新接口的所有實現都實現處置模式,即使它們沒有任何可用資源。