Standard vs Impl beim Implementieren von Schnittstellen in Java

34

Nach dem Lesen Sollten Paketnamen Singular oder Plural sein? Mir ist aufgefallen, dass ich noch nie eine richtige Debatte über eines meiner Lieblingsprobleme gesehen habe: die Benennung von Schnittstellenimplementierungen.

Angenommen, Sie haben eine Schnittstelle Order, die auf verschiedene Arten implementiert werden soll, aber es gibt nur die erste Implementierung, wenn das Projekt zum ersten Mal erstellt wird. Entscheiden Sie sich für DefaultOrderoder für OrderImpleine andere Variante, um die falsche Zweiteilung zu vermeiden? Und was machen Sie, wenn weitere Implementierungen folgen?

Und am wichtigsten ... warum?

Gary Rowe
quelle

Antworten:

59

Namen haben die Möglichkeit, Bedeutung zu vermitteln. Warum sollten Sie diese Gelegenheit mit Impl verpassen?

Zunächst einmal, wenn Sie immer nur eine Implementierung haben, tun , weg mit der Schnittstelle. Es verursacht dieses Benennungsproblem und fügt nichts hinzu. Schlimmer noch, es kann Probleme mit inkonsistenten Methodensignaturen in APIs verursachen, wenn Sie und alle anderen Entwickler nicht darauf achten, immer nur die Schnittstelle zu verwenden.

Vorausgesetzt, wir können davon ausgehen, dass jede Schnittstelle zwei oder mehr Implementierungen hat oder haben kann .

  • Wenn Sie im Moment nur eine haben und nicht wissen, inwiefern sich die andere unterscheidet, ist Default ein guter Anfang.

  • Wenn Sie gerade zwei haben, benennen Sie jede nach ihrem Zweck.

    Beispiel: Vor kurzem hatten wir eine konkrete Klasse Context (in Bezug auf eine Datenbank). Es wurde erkannt, dass wir in der Lage sein mussten, einen Kontext darzustellen, der offline war. Daher wurde der Name Context für eine neue Schnittstelle verwendet (um die Kompatibilität für alte APIs aufrechtzuerhalten), und eine neue Implementierung wurde erstellt, OfflineContext . Aber raten Sie mal, in was das Original umbenannt wurde? Das ist richtig, ContextImpl (yikes).

    In diesem Fall wäre DefaultContext wahrscheinlich in Ordnung , und die Leute würden es bekommen, aber es ist nicht so beschreibend, wie es sein könnte. Wenn es nicht offline ist , was ist es dann? Also gingen wir mit: OnlineContext .


Sonderfall: Verwenden des Präfix "I" bei Schnittstellen

Eine der anderen Antworten schlug vor, das Präfix I für Schnittstellen zu verwenden. Vorzugsweise müssen Sie nicht brauchen , dies zu tun.

Wenn Sie jedoch für benutzerdefinierte Implementierungen sowohl eine Schnittstelle als auch eine konkrete Primärimplementierung benötigen , die häufig verwendet wird, und der grundlegende Name dafür einfach zu einfach ist, um auf eine Schnittstelle allein zu verzichten, können Sie das Hinzufügen in Betracht ziehen "Ich" zur Benutzeroberfläche (obwohl es völlig in Ordnung ist, wenn es für Sie und Ihr Team immer noch nicht richtig ist).

Beispiel: Viele Objekte können ein "EventDispatcher" sein. Für APIs muss dies einer Schnittstelle entsprechen. Sie möchten jedoch auch einen einfachen Ereignis-Dispatcher für die Delegierung bereitstellen . DefaultEventDispatcher wäre in Ordnung, aber es ist etwas lang, und wenn Sie den Namen häufig sehen, ziehen Sie es möglicherweise vor, den Basisnamen EventDispatcher für die konkrete Klasse zu verwenden und IEventDispatcher für benutzerdefinierte Implementierungen zu implementieren:

/* Option 1, traditional verbose naming: */
interface EventDispatcher { /* interface for all event dispatchers */ }
class DefaultEventDispatcher implements EventDispatcher {
  /* default event dispatcher */
}

/* Option 2, "I" abbreviation because "EventDispatcher" will be a common default: */
interface IEventDispatcher { /* interface for all event dispatchers */ }
class EventDispatcher implements IEventDispatcher {
  /* default event dispatcher. */
}
Nicole
quelle
3
+1 für eine solide Antwort. Wie die Überlegungen, die dahinter stecken, Impl nicht zu verwenden.
Gary Rowe
2
+1 stimme absolut zu. Wenn Sie eine Schnittstelle außerhalb des Domänenkonzepts benennen, für das sie steht, kommt es nicht darauf an.
Rupjones
9
"Wenn Sie immer nur eine Implementierung haben", woher wissen Sie vorher, dass Sie nur eine Implementierung haben werden? "Jede Schnittstelle hat oder kann zwei oder mehr Implementierungen haben" ... bevor Sie zwei Implementierungen haben, haben Sie zuerst keine, die Sie eine haben, dann haben Sie zwei, ich meine, es kann einen Moment geben, in dem Sie nur eine Implementierung haben, nur vor der Implementierung des zweiten.
Tulains Córdova
2
@NickC Ich bin kein pedantinc über Semantik (ich bin ein Dichter und ich wusste es nicht einmal). Englisch ist nicht meine Muttersprache, deshalb kann ich nicht pedantisch sein. Ich sprach von fehlerhafter Logik. Sie verwenden Schnittstellen zur Entkopplung. Das erfordert keine bestimmte Anzahl von Implementierungen.
Tulains Córdova
4
if you will only ever have one implementation, do away with the interface- es sei denn, Sie möchten die Komponente testen. In diesem Fall möchten Sie möglicherweise diese Schnittstelle beibehalten, um MockOrder, OrderStub oder ähnliches zu erstellen.
JBRWilkinson
15

Ich entscheide die Benennung durch den Anwendungsfall der Schnittstelle.

Wenn die Schnittstelle zur Entkopplung verwendet wird , entscheide ich mich Implfür Implementierungen.

Wenn der Zweck der Schnittstelle die Abstraktion des Verhaltens ist , werden die Implementierungen entsprechend ihrer konkreten Vorgehensweise benannt. Ich füge oft den Schnittstellennamen hinzu. Also wenn die Schnittstelle aufgerufen wird Validator, benutze ich FooValidator.

Ich finde, das Defaultist eine sehr schlechte Wahl. Zunächst werden Code-Vervollständigungsfunktionen verschmutzt, da die Namen immer damit beginnen. Die andere Sache ist, dass sich ein Standard im Laufe der Zeit ändern kann. Was also als erstes ein Standard sein könnte, kann einige Zeit später ein veraltetes Feature sein. Entweder benennen Sie Ihre Klassen immer um, sobald sich die Standardeinstellungen ändern, oder Sie leben mit irreführenden Namen.

SpaceTrucker
quelle
8

Ich bin mit Nicoles Antwort einverstanden (insbesondere, dass die Schnittstelle in den meisten Fällen wahrscheinlich nicht erforderlich ist), aber zur Erörterung werde ich eine andere Alternative als OrderImplund ausschließen DefaultOrder: Verstecke die Implementierung hinter einer statischen Factory-Methode wie Orders.create(). Beispielsweise:

public final class Orders {
  public static Order create() {
    return new Order() {
      // Implementation goes here.
    };
  }
}

Bei diesem Ansatz kann es sich bei der Implementierung um eine anonyme innere Klasse handeln, oder es kann sich um eine private Klasse mit Defaultoder Implim Namen handeln, oder es kann ein ganz anderer Name verwendet werden. Unabhängig davon, welche Wahl getroffen wird, muss sich der Anrufer nicht darum kümmern, sodass Sie jetzt und später mehr Flexibilität erhalten, wenn Sie sich entscheiden, diese zu ändern.

Einige gute Beispiele für dieses Muster in der Praxis sind die Klassen java.util.Collectionsund java.util.concurrent.Executorsutility, deren Methoden versteckte Implementierungen zurückgeben. Wie Effective Java (in Punkt 1) erwähnt, kann dieses Muster dazu beitragen, das "konzeptionelle Gewicht" der API kleiner zu halten.

Andrew McNamee
quelle
+1 für einen interessanten Zusatzfall: anonyme Implementierungen.
Gary Rowe
1
Wird auch häufig in Guave verwendet .
Nicole
3

Ich gehe immer OrderImpleinfach so vor, weil es direkt nach dem OrderInterface in alphabetischer Reihenfolge angezeigt wird.

Ben Hoffstein
quelle
2
Und wie gehen Sie mit den laufenden weiteren Implementierungen um, für die spezielle Behandlung und so weiter?
Gary Rowe
1
Sie haben nach der ersten Implementierung gefragt. Sobald Sie mit der Implementierung von DomesticOrder, ForeignOrder oder was auch immer beginnen, werden sie nachdenklicher und unabhängig von ihrer Position im Alphabet benannt.
Ben Hoffstein
Ganz richtig - ich habe die ursprüngliche Frage bearbeitet, um diese neue Anforderung widerzuspiegeln.
Gary Rowe
Keine gute Idee Wenn es mehr als eine Implementierung geben wird.
Sadegh
0

Sie können die Schnittstelle mit dem Präfix I (IWhatever) benennen und dann die Implementierung Whatever vornehmen.

Offizielle Java-Code-Konventionen beschreiben diese Art der Benennung von Schnittstellen nicht, diese Art der Benennung hilft jedoch bei der Erkennung und Navigation.

Matthieu
quelle
Warum sollten Sie der Schnittstelle ein I voranstellen? Soll es die Erkennung in der Navigation unterstützen?
Gary Rowe
@Gary: Das Präfixieren von Schnittstellentypen mit I ist in der Delphi-Sprache eine gängige Konvention. In Delphi haben Klassentypen das Präfix T. Wir hätten also eine IOrder-Schnittstelle und eine TOrder-Standardimplementierung mit TSomethingOrder und TBullMarketOrder als spezifischen Implementierungen.
Marjan Venema
3
Ich habe gesehen, dass diese Konvention auch in der .NET-Entwicklung häufig verwendet wird. Vielleicht, weil die Dokumentation und Beispiele von Microsoft es verwenden.
Ben Hoffstein
5
Es scheint, dass @Renesis einen nützlichen Fall für die Verwendung in Java gefunden hat. Persönlich würde ich mich darauf verlassen, dass die IDE mir den Unterschied sagt, sonst hätten wir E für Enums, C für Klassen und alles weitgehend überflüssig.
Gary Rowe
0

Ich denke, dass Default in einigen Fällen sinnvoll sein kann, es aber hilfreicher wäre, die Implementierung zu beschreiben. Also, wenn Ihre Schnittstelle ist, UserProfileDAOdann können Ihre Implementierungen UserProfileSQLDAOoder UserProfileLDAPDAOoder so ähnlich sein.

jiggy
quelle
0

wenn möglich, benenne es nach was / wie es seine Sache macht.

Normalerweise ist es der schlechteste Ansatz, ihn nach der Art und Weise zu benennen, wie er verwendet werden soll.

Wenn es als Basisklasse für die Implementierung verwendet werden soll, können Sie BaseX oder AbstractX verwenden (wenn es abstrakt ist). Versuchen Sie jedoch, die Funktionen zu verfeinern, da Sie es bereits erstellt haben, wenn es nichts getan hätte Schnittstelle) .Wenn es die einfachstmögliche Funktionalität bietet und erwartet wird, dass es direkt verwendet wird (ohne es zu erweitern), wenn eine solche Funktionalität ausreicht, können Sie es SimpleX oder BasicX nennen.

Wenn es verwendet wird, sofern keine andere Implementierung bereitgestellt wird, nennen Sie es DefaultX

user470365
quelle
-2

Die meisten dieser Antworten beschreiben, was getan wird, aber nicht warum.

Was ist mit den OO-Prinzipien passiert? Was sagt Impl zu einer Klasse und wie benutze ich sie? Sie sollten sich Gedanken darüber machen, wie die Verwendung und nicht wie sie aufgebaut ist.

Was ist ein PersonImpl, wenn ich eine Personenschnittstelle erstelle? Bedeutungslos. Zumindest sagt mir DefaultPerson, wer auch immer codiert hat, er sollte keine Schnittstelle erstellen.

Schnittstellen tippen auf Polymorphismus und sollten als solche verwendet werden.

user108620
quelle
1
In der akzeptierten Antwort von @NickC wird detailliert beschrieben, was und warum getan wird.
Gary Rowe