Sollte eine Schnittstelle eine andere Schnittstelle erben


71

Ich kann anscheinend keine Antwort darauf finden und möchte nur sicherstellen, dass es sich um einen in Ordnung befindlichen Codierungsstandard handelt. Ich habe eine Schnittstelle A, die von vielen verschiedenen Klassen verwendet wird, und möchte nicht, dass sich die Schnittstelle Aändert. Ich bin auf eine neue Anforderung gestoßen, die erfordert, dass viele der Klassen, die Interface implementieren A, eine Aufzählung benötigen , aber nicht alle Klassen benötigen diese Aufzählung. Ich möchte nicht, dass die Klassen, die diese neue Aufzählung nicht benötigen, diese neue Funktionalität implementieren. Also habe ich eine Schnittstelle erstellt B, die die neue Aufzählung enthält, die ich hinzufügen musste. Ich habe dann die Schnittstelle dazu gebracht, Bdie Schnittstelle zu erben, Aund dies ist mein Anliegen. Ist es in Ordnung, wenn eine Schnittstelle eine andere Schnittstelle erbt ? Um mit meinen Änderungen fortzufahren, habe ich dann die Klassen geändert, die die neue Aufzählung zum Implementieren der Schnittstelle benötigtenBanstelle der Schnittstelle, Ada sie von der Schnittstelle geerbt wurde B. Ich habe darüber nachgedacht, beide Schnittstellen in meinen Klassen zu implementieren, die sie benötigen, aber ich verwende die Schnittstelle im gesamten Code und möchte nur eine Schnittstelle zum Durchsuchen von Klassen verwenden und nicht zwei.

Ich hoffe, das war klar genug (wahrscheinlich zu lang), aber wenn mir jemand einen Rat geben kann, entweder mache ich es richtig oder ich mache es falsch, lass es mich wissen.

Vielen Dank!

Antworten:


80

Die Schnittstellenvererbung ist ein hervorragendes Werkzeug, obwohl Sie sie nur verwenden sollten, wenn die Schnittstelle B die Schnittstelle A wirklich ersetzt, und nicht nur, um lose verwandte Verhaltensweisen zu aggregieren.

Es ist schwer zu sagen, ob es für Ihren speziellen Fall geeignet ist, aber es ist nichts Falsches daran, die Praxis im Prinzip anzuwenden. Sie sehen es die ganze Zeit in den erstklassigen APIs. Um nur ein allgemeines Beispiel aus dem .NET Framework auszuwählen:

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

2
Ich habe ursprünglich vorgeschlagen, das Liskov-Substitutionsprinzip anzuwenden. Rückblickend ist das nicht wirklich relevant. Die meisten Anforderungen des LSP (Aufrechterhaltung von Invarianten, Einschränkungen der Vor- und Nachbedingungen) gelten tatsächlich nur für konkrete Implementierungen, nicht für Schnittstellen. Das allgemeine Prinzip der Substituierbarkeit sollte jedoch weiterhin die Entscheidungen über die Vererbung von Schnittstellen leiten.
Jeff Sternal

Liskov-Substitutionsprinzip: Es ist wichtig sicherzustellen, dass Schnittstelle B Schnittstelle A vollständig ersetzen kann. Andernfalls erhalten Sie Funktionen, die Sie implementieren müssen und die Sie nicht möchten. Dies führt zu zusätzlichem Code, den Sie nicht möchten, wodurch die Software weniger stabil wird.
Chad Crowe

Ich habe festgestellt, dass es gut funktioniert, wenn eine Fassade durch eine vorhandene Abhängigkeit ersetzt wird, für die jetzt eine zweite Implementierung erforderlich ist. Anstatt die ursprüngliche Klasse und alle Tests zu ändern, um eine Fabrik zu benötigen, wird die Fassade mit der Fabrik behandelt und das gewünschte Ergebnis zurückgegeben. Ich verwende Markierungsschnittstellen, um die zwei verschiedenen Schnittstellen in die Fabrik zu injizieren, die die Fassade verwenden wird. Nur die Fabrik kennt die "Marker" -Schnittstellen. Nicht ideal, aber es gefällt mir besser, als die vorhandenen Klassen zu ändern.
DanCaveman

21

Überlegen Sie, ob die Schnittstellen logisch gekoppelt werden sollen, und verwenden Sie unbedingt die Vererbung, wenn Sie der Meinung sind, dass sie gut miteinander funktionieren.

Schauen wir uns ein Beispiel an.

public interface IScanner
{
    void Scan();
}

public interface IPrinter
{
    void Print();
}

Drucker und Scanner sind häufig separate Objekte mit jeweils eigenen Funktionen. Diese beiden Geräte werden jedoch häufig auf demselben Gerät gekoppelt.

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

Es ist sinnvoll, dass IPhotocopier von IScanner und IPrinter erbt, da der Fotokopierer nun zusätzlich zu seiner primären Rolle als Kopierer entweder als Scanner oder als Drucker (den er enthält) verwendet werden kann.

Schauen wir uns nun eine weitere Schnittstelle an.

public interface IBlender
{
    void Blend();
}

Es wäre nicht sinnvoll zuzulassen, dass IBlender von einer der früheren Schnittstellen geerbt wird (wie würden Sie sie nennen? IBlendingScanner?).

Wenn Sie Ihrer neuen Schnittstelle keinen sinnvollen Namen geben können, weist dies möglicherweise darauf hin, dass Sie in dieser Instanz möglicherweise keine Vererbung verwenden möchten.

Es ist eine schlechte Idee, einige Schnittstellen wie IDisposable zu erben, da dies alle Implementierungen Ihrer neuen Schnittstelle dazu zwingt, das Entsorgungsmuster zu implementieren, auch wenn sie keine verfügbaren Ressourcen haben.


12

Technisch gesehen erben Schnittstellen nicht voneinander. Was wirklich passiert, wenn Sie eine IFooerben, von der erbt IBar, sagen Sie, dass jede Klasse, die implementiert, IFooauch implementiert werden muss IBar.

interface IBar
{
    void DoBar();
}

interface IFoo : IBar
{
    void DoFoo();
}

In diesem Beispiel verfügt die IFooSchnittstelle über keine DoBar()Methode. Meistens spielt die Unterscheidung keine Rolle, aber sie kann Sie beißen, wenn Sie die Reflexion auf einer Schnittstelle anstatt auf einer Klasse verwenden.


5
Selbst angesichts des von Ihnen beschriebenen Verhaltens (das in Phil Haacks jüngster Kolumne haacked.com/archive/2009/11/10/… ausführlich beschrieben wurde ) finde ich es unangemessen verwirrend zu sagen, dass Schnittstellen nicht voneinander erben 'in C #, wenn die C # -Referenzdokumentation diese Terminologie sogar verwendet, wie in "Eine Schnittstelle kann von einer oder mehreren Basisschnittstellen erben." ( msdn.microsoft.com/en-us/library/87d83y5b%28VS.80%29.aspx ) Ich möchte nur sagen, dass sich die Schnittstellenvererbung in C # anders verhält als die Klassenvererbung, wenn geerbte Mitglieder reflektiert werden.
Jeff Sternal

1
Fair genug. Ich denke, es hängt davon ab, wie "erben" definiert ist. Sicherlich ist die C # -Syntax dieselbe, egal ob es sich um Klassen oder Schnittstellen handelt. Wenn Sie jedoch davon ausgehen, dass die Vererbung die Mitglieder des Elternteils einschließt, stimmt Ihr mentales Modell nicht mit der Realität überein, wenn Sie über Schnittstellen sprechen.
Joel Mueller

6

Es ist sicherlich möglich, einen Vererbungsbaum von Schnittstellen und sogar eine "Mehrfachvererbung" mit Schnittstellen zu haben. Ob es das Richtige ist oder nicht, hängt von den jeweiligen Schnittstellen ab. Wenn es wirklich so ist, dass Schnittstelle B eine Erweiterung oder Verfeinerung von Schnittstelle A ist, dann ist Vererbung sinnvoll, aber wenn die neue Aufzählung weitgehend nichts mit dem von Schnittstelle A ausgedrückten Konzept zu tun hat, würde ich sie zu zwei separaten Schnittstellen machen und die Klassen haben das müssen beide Schnittstellen implementieren.


3

IMO das ist genau der richtige Ansatz, ich sehe kein Problem damit.


2

Ich denke, Datenbanken bieten immer eine großartige Möglichkeit, Schnittstellen zu demonstrieren. Wenn Sie also überlegen, ob eine Schnittstelle eine andere Schnittstelle erben soll, sehen Sie sich Folgendes an:

IMySqlDatabase : IDatabase
MySqlDatabase : IMySqlDatabase

IMsSqlDatabase : IDatabase
MsSqlDatabase : IMsSqlDatabase

Eine MySqlDatabase ist eine IMySqlDatabase und eine IMySqlDatabase ist eine IDatabase.

Wenn Sie jetzt Änderungen an Ihrer IDatabase-Schnittstelle vornehmen müssen, können die Enkelkinder (die konkreten Datenbankklassen) die Vorteile nutzen, aber Sie müssen MySQL UND MsSQL (oder vielleicht sogar noch mehr DBMS-Schnittstellen) nicht erweitern. Gleichzeitig können Sie in Ihrem Middle Man (IMsSqlDatabase) weiterhin Schnittstellenfunktionen haben, die eine MySQL- oder Oracle-Datenbank nicht unterstützen würde.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.