So implementieren Sie nur einen Teil einer Schnittstelle

13

Bei der Entwicklung in OOP wird manchmal eine Schnittstelle / ein Vertrag von einer Bibliothek vergeben, die Sie nicht ändern können. Nennen wir diese Schnittstelle J.

Jetzt haben Sie ein Objekt der Klasse A, das Objekte verwendet, die diese Schnittstelle implementieren. Innerhalb von A wird nur ein kleiner Teil der Definitionen der Schnittstelle benötigt. Einige der Objektklassen werden von mir während des Projekts erstellt (nennen wir eine von ihnen Typ D), daher ist die Implementierung von allem in Interface J mit einem Mehraufwand verbunden.

Ich möchte eine Teilmenge der Funktionalität in Schnittstelle J implementieren, aber meine bisherigen Lösungen erfüllen mich nicht:

  • Das Implementieren jedes Aspekts von J und das anschließende Auslösen von "notImplementedExceptions" führt zu einer Fehlinformation des Benutzers meiner Objekte: Es scheint, dass meine Objekte vom Typ D der Schnittstelle J entsprechen, aber nicht - und andere Konsumenten meiner Objekte (die Objekte akzeptieren, die die Schnittstelle implementieren) J) kann mich nicht auf die Integrität meiner Objekte verlassen.
  • Das Implementieren einer neu definierten Schnittstelle verbietet mir die Verwendung von Objekten, die nur die Schnittstelle J implementieren, obwohl die Schnittstelle J vollständig mit meiner eigenen Schnittstelle kompatibel ist.
  • Wenn meine benutzerdefinierten Objekte die Schnittstelle J implementieren, würde dies einen erheblichen Overhead verursachen, da sie nicht alle diese Funktionen benötigen.

Wenn ich in der Lage wäre, die Schnittstelle J zu ändern, würde ich eine "Superschnittstelle" K erstellen, die diese Teilmenge der Funktionalität der Schnittstelle J aufweist, und die Schnittstelle J von der Schnittstelle K erben lassen. Aber ich kann die Schnittstelle J nicht ändern.

Was ist eine objektorientierte Lösung für dieses Problem? Implementiert die beste Lösung immer noch "nur" Interface J? Oder gibt es OOP-Möglichkeiten, ein Interface zu "überklassifizieren", ohne es zu verändern?

vstrien
quelle
1
Sehen Sie, wie die * Adapterklassen in Swing dies tun.

Antworten:

8

Wenn Sie die Schnittstelle J nicht steuern, stecken Sie fest.

Sie können aus Gründen der Übersichtlichkeit Ihre eigene Schnittstelle subJ und Schnittstelle J implementieren und subJ in Ihrem eigenen Code verwenden, um zu verdeutlichen, dass die zusätzlichen Methoden in Schnittstelle J nicht erforderlich sind, aber ich denke, das bringt Ihnen nicht viel

Wenn möglich, implementieren Sie die gesamte Schnittstelle J vollständig

Wenden Sie sich nach Möglichkeit an den Eigentümer von Schnittstelle J, und bitten Sie ihn, sie zu ändern, um sie besser an Ihre Zwecke anzupassen

Steven A. Lowe
quelle
2
Als Kompromiss: Sie können den Eigentümer der Schnittstelle J bitten, die kleinere Schnittstelle SubJ zu erben.
k3b
26

Der springende Punkt bei der Implementierung einer Schnittstelle besteht darin, einen festen Vertrag zwischen dem Anrufer und dem Angerufenen herzustellen, der sich nie ändert, während die Implementierungsdetails variieren können.

Indem Sie die Schnittstelle J implementieren, teilen Sie der Außenwelt mit, was Sie tun können.

Wenn Sie nicht die Hälfte von dem benötigen, was J tut, sollten Sie entweder die Schnittstelle wirklich unterteilen, da sie nicht so klein ist, wie sie sein könnte, oder Sie müssen NotImplementedExceptiondie Methoden und Eigenschaften verwenden, die Sie nicht benötigen. Dies ist jedoch nicht die beste Lösung, da dadurch die Erwartungen der Benutzer an die Funktionsweise Ihres Codes gestört werden.

ChrisF
quelle
4

Wenn Ihre Consumer-Klasse A nicht die gesamte Schnittstelle J benötigt, würde ich vorschlagen, eine neue Schnittstelle (nennen Sie sie K) zu erstellen, die alle erforderlichen Informationen ausführlich beschreibt.

Dies ist ein klares Signal für alle, die Klasse A verwenden, welche Vertragspartei sie haben. Dadurch verbessern sich die Wiederverwendungschancen. Wenn jemand ein Objekt bereitstellen muss, das eine große und komplexe Schnittstelle implementiert, um etwas relativ Einfaches zu tun, wird er wahrscheinlich das schreiben, was Klasse A selbst tut.

Damit Klasse A Objekte verwenden kann, die nur die Schnittstelle J implementieren, können Sie eine Wrapper-Klasse bereitstellen, die die Schnittstelle K implementiert und entsprechende Aufrufe an ein Mitglied der Schnittstelle J weiterleitet.

Paul Butcher
quelle
3

Obwohl ich den vorhandenen Antworten zustimme, die besagen, dass Sie J entweder vollständig (mit Ausnahmen für nicht implementierte Methoden) oder überhaupt nicht implementieren müssen, ist eine mögliche Problemumgehung folgende:

  • Erstellen Sie eine kleinere Schnittstelle K, die eine Teilmenge von J ist und die erforderlichen Methoden implementiert.
  • Erstellen Sie einen Wrapper für A, der Objekte akzeptiert, die J und K implementieren.
  • Im Fall eines übergebenen J einfach auf die Instanz von A durchschieben.
  • Im Fall eines übergebenen K instanziieren Sie eine anonyme Implementierung von J, indem Sie nur die Methoden von K nach J verbinden, die dort vorhanden sind. Übergeben Sie das Ergebnis an die Instanz von A.

Bitte beachten Sie, dass dies eine ziemlich hässliche Lösung ist, da ein Wrapper erforderlich ist, der mehrere Dinge akzeptiert, die im Wesentlichen dasselbe sind. Aber es wird folgendes erreicht:

  • Keine Änderungen an J oder A.
  • Keine "exponierten" Implementierungen von J, die unvollständig sind.

Wenn Ihre OO-Sprache keine anonyme Schnittstelleninstanziierung zulässt, können Sie eine Dummy-Implementierung der Schnittstelle erstellen und diese instanziieren.

Deckard
quelle
1

Wie lange würde die Implementierung der gesamten Schnittstelle dauern?

Wenn Sie längere Zeit benötigen, deutet dies auf ein Designproblem hin, und ich rate Ihnen (wie Steven A. vor mir), sich mit dem Eigentümer in Verbindung zu setzen und zu prüfen, ob es in Zukunft geändert werden kann.

Verwenden Sie die Schnittstelle in Ihrem eigenen Code, unabhängig von der Bibliothek der Schnittstelle?

Wie von Steven A. vorgeschlagen, können Sie Ihre eigene Benutzeroberfläche so verwenden, wie Sie es in Ihrem eigenen Code sehen möchten. Dies hält zumindest Ihren Inhouse-Code sauber. Sie können dies auch an den Eigentümer als die zu erwartende Schnittstelle senden. Vielleicht stimmt er Ihnen zu, oder vielleicht kann er Ihnen erklären, warum die Schnittstelle nicht aufgeteilt werden sollte.

Würde Ihre Implementierung jemals die nicht verwendeten Mitglieder der Schnittstelle benötigen?

Falls Sie erwarten können, dass sie niemals aufgerufen werden, da sie nicht unterstützt werden, bevorzuge ich die NotSupportedException. Dies gibt einen klaren Hinweis darauf, dass Sie diese Schnittstelle niemals unterstützen werden, anstatt sie nicht zu implementieren.

Steven Jeuris
quelle
Eine gute Idee, NotSupportedExceptiones zu verwenden, macht deutlich, dass Sie diese Methode nicht unterstützen.
ChrisF
@ChrisF: Ich verwende nur NotImplementedExceptionfür Produktionscode, wo ich sonst schreiben würde "TODO: Implementiere dies" Leider werden sie nicht in der Aufgabenliste von Visual Studio angezeigt . Aber in der Praxis lasse ich kaum eine Methode länger als zwei Tage unimplementiert. Resharper zeigt diese Ausnahmen auch in Fettdruck an (zumindest bei meinem Setup). :)
Steven Jeuris
0

Implementieren Sie das, was Sie benötigen, und werfen Sie eine Ausnahme NoImplementedException auf die anderen.

Dies ist eine Frage der Verwendung. Wenn Sie die Schnittstelle nicht verwenden oder wissen, dass Ihr Code die Schnittstelle nicht verwendet, investieren Sie keine Zeit in diese, da Sie keine Zeit in redundanten Code investieren müssen.

Arbeiten Sie für die anstehende Aufgabe und behalten Sie die Übersicht, die andere Benutzer benötigen, wenn sie die Benutzeroberfläche verwenden möchten.

Viele Schnittstellen in Java sind nicht vollständig implementiert.

Dies ist der Apache-Ansatz für Dinge: http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/NotImplementedException.html

Anzeigename
quelle
Vielleicht argumentieren Sie, warum Sie glauben, dass dies eine richtige Lösung ist. Dies fügt der Frage nichts hinzu.
Steven Jeuris
Es ist nicht notwendig, alles zu implementieren, da es nicht wirklich notwendig ist, alles zu erklären. Wäre dies der Fall, wären Teile der Schnittstelle überflüssig, würde er sie sofort abholen.
Anzeigename