2009-11-06 7 views
45

Non riesco a trovare una risposta su questo e voglio solo essere sicuro che sia uno standard di codifica ok. Ho un'interfaccia A che viene utilizzata da molte classi diverse e non voglio che l'interfaccia A cambi. Mi sono imbattuto in un nuovo requisito che richiederà l'enumerazione di molte delle classi che implementano l'interfaccia A, ma non tutte le classi hanno bisogno di questo enum. Non voglio le classi che non richiedono questo nuovo enum per implementare questa nuova funzionalità. Così ho creato l'interfaccia B che contiene il nuovo enum che dovevo aggiungere. Ho quindi realizzato un'interfaccia E1 di interfaccia B di A e questa è la mia preoccupazione, è ok per un'interfaccia per ereditare un'altra interfaccia? Per continuare con le mie modifiche, ho quindi modificato le classi che avevano bisogno del nuovo enum per implementare l'interfaccia B invece dell'interfaccia A poiché era ereditata dall'interfaccia B. Ho pensato di implementare entrambe le interfacce nelle mie classi che ne avevano bisogno, ma sto usando il Interfaccia su tutto il codice e vorrebbe usare solo un'interfaccia per guardare attraverso le classi e non due.Se un'interfaccia eredita un'altra interfaccia

Spero che questo sia stato abbastanza chiaro (probabilmente troppo lungo) ma se qualcuno può darmi qualche consiglio su questo o sto facendo bene o sto sbagliando per favore fammi sapere.

Grazie!

risposta

60

L'ereditarietà dell'interfaccia è uno strumento eccellente, anche se dovresti usarlo solo quando l'interfaccia B è veramente sostituibile all'interfaccia A, non solo per aggregare comportamenti vagamente correlati.

È difficile dire se è appropriato per il caso specifico, ma non c'è nulla di sbagliato nella pratica in linea di principio. Lo vedi sempre nelle API di prim'ordine. Per scegliere solo un esempio comune, dal .NET framework:

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

Inizialmente avevo suggerito di applicare il principio di sostituzione di Liskov. In retrospettiva, questo non è veramente rilevante. La maggior parte dei requisiti dell'LSP (mantenimento delle invarianti, restrizioni sulle condizioni pre e post) sono in realtà applicabili solo a implementazioni concrete, non a interfacce. Detto questo, il principio generale di sostituibilità dovrebbe ancora guidare le decisioni di ereditarietà dell'interfaccia. –

+0

Principio di sostituzione di Liskov: è importante assicurarsi che l'interfaccia B possa sostituire completamente l'interfaccia A. Altrimenti, si arriverà alla funzionalità che è necessario implementare e che non si desidera. Questo porta a un codice extra che non vuoi, il che rende il software meno stabile. –

2

IMO questo è esattamente l'approccio giusto, non vedo alcun problema con esso.

6

È certamente possibile avere un albero di ereditarietà di interfacce e persino "eredità multipla" con interfacce. Se sia la cosa giusta da fare o meno dipende dalle interfacce in questione. Se è davvero il caso che l'interfaccia B sia un'estensione o un perfezionamento dell'interfaccia A, allora l'ereditarietà ha senso, ma se la nuova enumerazione è largamente scollegata dal concetto espresso dall'interfaccia A, vorrei renderli due interfacce separate e avere le classi che devono implementare entrambe le interfacce.

+0

Buon punto e sì in questa situazione sono correlati. – ajrawson

5

Tecnicamente parlando, le interfacce non ereditano l'una dall'altra. Ciò che accade realmente quando si crea un eredita da , si dice che qualsiasi classe che implementa IFoo deve implementare anche IBar.

interface IBar 
{ 
    void DoBar(); 
} 

interface IFoo : IBar 
{ 
    void DoFoo(); 
} 

In questo esempio, l'interfaccia IFoo non ha un metodo DoBar(). La maggior parte delle volte la distinzione non ha importanza, ma può morderti quando usi la riflessione su un'interfaccia piuttosto che su una classe.

+3

Anche dato il comportamento che hai descritto (descritto in dettaglio eccellente nella recente colonna di Phil Haack, http://haacked.com/archive/2009/11/10/interface-inheritance-esoterica.aspx), penso che sia ingiustificatamente confuso dire "le interfacce non si ereditano l'una dall'altra" in C# quando la documentazione di riferimento di C# utilizza addirittura quella terminologia, come in "Un'interfaccia può ereditare da una o più interfacce di base". (http://msdn.microsoft.com/en-us/library/87d83y5b%28VS.80%29.aspx) Preferirei solo dire che, in C#, l'ereditarietà dell'interfaccia si comporta diversamente dall'ereditarietà delle classi quando riflette i membri ereditati . –

+1

Abbastanza giusto. Immagino che dipenda da come viene definito "inherit". Certamente la sintassi C# è la stessa sia che si tratti di classi o interfacce. Ma se pensi all'eredità come includendo i membri del genitore, allora il tuo modello mentale non corrisponde alla realtà quando parli di interfacce. –

1

Penso database forniscono sempre un ottimo modo per dimostrare le interfacce, in modo da prendere in considerazione se l'interfaccia dovrebbe ereditare un altro sguardo interfaccia al seguente,

IMySqlDatabase : IDatabase 
MySqlDatabase : IMySqlDatabase 

IMsSqlDatabase : IDatabase 
MsSqlDatabase : IMsSqlDatabase 

Un MySqlDatabase è un IMySqlDatabase e un IMySqlDatabase è un IDatabase.

Ora, se è necessario apportare modifiche all'interfaccia IDatabase, i nipoti (le classi di database conrete) possono trarre vantaggio, ma non sarà necessario espandere MySQL AND MsSQL (o forse anche più interfacce DBMS).Allo stesso tempo nel tuo intermediario (IMsSqlDatabase) puoi ancora avere caratteristiche di interfaccia che un DB MySQL o Oracle non supporterebbe.

13

Considerare se le interfacce devono essere accoppiate in modo logico e se si ritiene che funzionino bene tra loro, quindi utilizzare assolutamente l'ereditarietà.

Consente di guardare un esempio;

public interface IScanner 
{ 
    void Scan(); 
} 

public interface IPrinter 
{ 
    void Print(); 
} 

stampanti e scanner sono spesso oggetti separati, ciascuno con la propria funzionalità tuttavia questi due dispositivi sono spesso accoppiati nello stesso dispositivo;

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

ha senso che IPhotocopier dovrebbe ereditare da iScanner e IPrinter quanto ciò permette ora la fotocopiatrice da utilizzare come uno scanner o una stampante (che contiene) in aggiunta alla sua cilindro principale come copiatrice.

Ora consente di guardare un'altra interfaccia;

public interface IBlender 
{ 
    void Blend(); 
} 

Non avrebbe senso per consentire IBlender a essere ereditata da una delle interfacce precedenti (cosa vorresti chiamarli? IBlendingScanner?).

Se non è possibile dare un nome ragionevole alla nuova interfaccia, è possibile che in questa istanza non si desideri utilizzare l'ereditarietà.

È una cattiva idea ereditare alcune interfacce come IDisposable, poiché ciò impone a tutte le implementazioni della nuova interfaccia di implementare lo schema di smaltimento anche se non dispongono di risorse disponibili.

Problemi correlati