Frage

, wenn in Java Programmierung ich praktisch immer, nur aus Gewohnheit, schreiben Sie etwas wie folgt aus:

public List<String> foo() {
    return new ArrayList<String>();
}

Die meiste Zeit ohne auch nur darüber nachzudenken. Nun ist die Frage: soll ich immer die Schnittstelle als Rückgabetyp angeben? Oder ist es ratsam, die eigentliche Implementierung der Schnittstelle zu verwenden, und wenn ja, unter welchen Umständen?

Es ist offensichtlich, dass mit Hilfe der Schnittstelle viele Vorteile hat (das ist, warum es da ist). In den meisten Fällen ist es nicht wirklich wichtig, was die konkrete Umsetzung wird von einer Bibliotheksfunktion verwendet. Aber vielleicht gibt es Fälle, in denen es keine Rolle spielt. Zum Beispiel, wenn ich weiß, dass ich in erster Linie die Daten in der Liste zufällig zugreifen, wäre ein LinkedList schlecht sein. Aber wenn meine Bibliothek Funktion nur die Schnittstelle zurückgibt, ich weiß es einfach nicht. Um auf der sicheren Seite sein könnte ich auch die Liste explizit über zu einem ArrayList kopieren müssen:

List bar = foo();
List myList = bar instanceof LinkedList ? new ArrayList(bar) : bar;

, aber das scheint nur schrecklich und meine Mitarbeiter würde wahrscheinlich lynchen mich in der Cafeteria. Und das zu Recht.

Was denkt ihr? Was sind Ihre Richtlinien, wann Sie neigen dazu, in Richtung der abstrakten Lösung, und wenn Sie sich für mögliche Leistungssteigerungen Details der Implementierung tun offenbaren?

War es hilfreich?

Lösung

  

Zum Beispiel, wenn ich weiß, dass ich   Zugriff auf in erster Linie um die Daten in der Liste   zufällig, wäre ein LinkedList schlecht sein.   Aber wenn meine Bibliotheksfunktion nur   die Schnittstelle zurückgibt, kann ich einfach nicht   kennt. Um auf der sicheren Seite zu sein, ich könnte   auch müssen die Liste explizit kopieren   über an einem Arraylist.

Wie alle anderen erwähnt wurde, müssen Sie fast egal, wie die Bibliothek, die die Funktionalität implementiert hat, Kupplung und zunehmende Wartbarkeit der Bibliothek zu reduzieren.

Wenn Sie, als Bibliothek Client, nachweisen können, dass die Umsetzung schlecht für Ihren Anwendungsfall ausführt, können Sie dann kontaktieren Sie die verantwortliche Person und diskutieren über den besten Weg zu folgen (eine neue Methode für diesen Fall oder nur die Änderung die Implementierung).

Wie gesagt, Ihr Beispiel stinkt der vorzeitigen Optimierung.

Wenn die Methode ist oder kann kritisch sein, könnte es die Details der Implementierung in der Dokumentation erwähnen.

Andere Tipps

Gibt die entsprechende Schnittstelle Implementierungsdetails zu verbergen. Ihre Kunden sollten nur darum kümmern, was Ihr Objekt bietet, nicht, wie Sie es umgesetzt. Wenn Sie mit einem privaten Arraylist starten, und sonst später auf, dass etwas entscheiden (zum Beispiel LinkedLisk, Skip-Liste, etc.) ist besser geeignet Sie die Implementierung ohne Auswirkung auf Kunden ändern können, wenn Sie die Schnittstelle zurück. Sobald Sie eine konkrete Art Rückkehr die Möglichkeit verloren geht.

In der OO-Programmierung, wir wollen so viel wie möglich die Daten kapseln. Wie bitte so weit wie möglich die tatsächliche Umsetzung, die Typen so hoch wie möglich zu abstrahieren.

In diesem Zusammenhang möchte ich antworten nur zurückgeben, was sinnvoll ist, . Ist es sinnvoll, überhaupt für den Rückgabewert macht die konkrete Klasse zu sein? Aka in Ihrem Beispiel, fragen Sie sich: wird jemand eine LinkedList spezifische Methode auf dem Rückgabewert von foo

  • Wenn nein, nur verwenden, um die übergeordnete Schnittstelle. Es ist viel flexibler und ermöglicht es Ihnen, das Backend zu ändern
  • Wenn ja, fragen Sie sich: kann ich meinen Code Refactoring die übergeordnete Schnittstelle zurückzukehren? :)

Je abstrakter ist der Code, desto weniger Änderungen Ihres tun müssen, wenn ein Backend zu ändern. So einfach ist das.

Wenn auf der anderen Seite, können Sie die Rückgabewerte an die konkreten Klasse Gießen am Ende, auch das ist ein starkes Zeichen, dass Sie wahrscheinlich stattdessen die konkrete Klasse zurückgeben sollten. Die Benutzer / Mitspieler sollten nicht über mehr oder weniger implizite Verträge müssen wissen:., Wenn Sie die konkreten Methoden verwenden müssen, nur um die konkrete Klasse zurückkehren, für Klarheit

Auf den Punkt gebracht: Code abstrakt , sondern explizit :)

Ohne Lage, es zu rechtfertigen, mit Unmengen von CS Anführungszeichen (Ich bin Autodidakt), ich habe immer das Mantra von „Accept die am wenigsten abgeleitet, kehren die meisten abgeleitet“ gegangen, als Klassen entwerfen und es stand mich gut über die Jahre.

Ich denke, das bedeutet in Bezug auf der Schnittstelle im Vergleich zu Beton Rückkehr ist, dass, wenn Sie Abhängigkeiten zu reduzieren versuchen und / oder zu entkoppeln, die Schnittstelle Rückkehr im Allgemeinen nützlich ist. Wenn jedoch die konkrete Klasse implementiert mehr als diese Schnittstelle ist es in der Regel sinnvoller zu den Anrufern Ihrer Methode, um die konkrete Klasse zurück zu erhalten (dh des „am weitesten abgeleitete“), anstatt aribtrarily sie beschränken eine Teilmenge der dieses Objekts Funktionalität zurückgegeben - es sei denn, Sie eigentlich Notwendigkeit , sie zu beschränken. Dann wieder, könnte man auch erhöhen nur die Reichweite der Schnittstelle. Unnötig Beschränkungen wie dies ich gedankenlos Abdichtung von Klassen zu vergleichen; man weiß nie. Nur um ein wenig über die frühere Teil dieses Mantra (für andere Leser) zu sprechen, akzeptieren die zuletzt auch ein Höchstmaß an Flexibilität abgeleitet für Anrufer Ihrer Methode gibt.

-Oisin

In der Regel für eine öffentlich zugängliche Schnittstelle wie APIs, die Schnittstelle Rückkehr (wie List) über die konkrete Umsetzung (wie ArrayList) wäre besser.

Die Verwendung eines ArrayList oder LinkedList ist eine Implementierung Detail der Bibliothek, die für den häufigste Anwendungsfall dieser Bibliothek in Betracht gezogen werden sollte. Und natürlich intern, private Methoden mit LinkedLists Handoffs würde nicht unbedingt eine schlechte Sache sein, wenn es bietet die Möglichkeit, die die Verarbeitung einfacher machen würde.

Es gibt keinen Grund, dass eine konkrete Klasse sollte nicht in der Umsetzung verwendet werden, es sei denn es einen guten Grund, dass einige andere List Klasse zu glauben, ist später verwendet werden würde. Aber dann wieder, die Implementierungsdetails zu ändern sollte nicht so schmerzhaft sein, solange die Öffentlichkeit gerichtete Teil ist gut gestaltet.

Die Bibliothek selbst sollte eine Blackbox zu den Verbrauchern sein, so dass sie wirklich nicht darum kümmern müssen, was intern passiert. Das bedeutet auch, dass die Bibliothek sollte so ausgelegt sein, dass sie ausgelegt ist in der Art und Weise verwendet wird es bestimmt ist.

In der Regel gebe ich nur interne Implementierungen zurück, wenn ich in einigen privaten bin, Innenleben einer Bibliothek, und selbst dann nur sparsam. Denn alles, was von außen meiner Modul I Schnittstellen genannt Öffentlichkeit und wahrscheinlich zu verwenden, und auch die Fabrik-Muster.

Mit Schnittstellen in einer solchen Art und Weise hat sich als sehr zuverlässig sein, wieder verwendbaren Code zu schreiben.

Die wichtigste Frage wurde bereits beantwortet und Sie sollten immer die Schnittstelle verwenden. Ich würde jedoch genau wie auf Kommentar

  

Es ist offensichtlich, dass mit Hilfe der Schnittstelle viele Vorteile hat (das ist, warum es da ist). In den meisten Fällen ist es nicht wirklich wichtig, was die konkrete Umsetzung wird von einer Bibliotheksfunktion verwendet. Aber vielleicht gibt es Fälle, in denen es keine Rolle spielt. Zum Beispiel, wenn ich weiß, dass ich in erster Linie die Daten in der Liste zufällig zugreifen, wäre ein LinkedList schlecht sein. Aber wenn meine Bibliothek Funktion nur die Schnittstelle zurückgibt, ich weiß es einfach nicht. Um auf der sicheren Seite sein, ich könnte auch die Liste explizit über zu einem Arraylist kopieren muß.

Wenn Sie eine Datenstruktur zurückkehren, die Sie wissen, schlechtes Zufallszugriffsverhalten haben - O (n) und in der Regel eine Menge von Daten - es gibt auch andere Schnittstellen, die Sie anstelle von List, wie Iterable werden sollen spezifiziert, so dass jeder mit die Bibliothek vollständig bewusst sein, dass nur sequenzieller Zugriff verfügbar ist.

die richtige Art Picking ist zurück nicht nur um die Schnittstelle gegenüber der konkreten Umsetzung, sondern auch um die richtige Schnittstelle auswählen.

Es spielt keine Rolle, dass alle viel, ob eine API-Methode eine Schnittstelle oder eine konkrete Klasse zurückgibt; trotz allem, was jeder hier sagt, Sie so gut wie nie die implementiation Klasse ändern, sobald der Code geschrieben wird.

Was viel wichtiger ist: immer mit minimalem Umfang Schnittstellen für Ihre Methode Parameter ! Auf diese Weise haben die Kunden maximale Freiheit und Klassen können Sie Ihren Code über nicht einmal wissen.

Wenn eine API-Methode ArrayList gibt, habe ich absolut keine Skrupel mit dem, aber, wenn es verlangt eine ArrayList (oder alle gemeinsam, Vector) Parameter, halte ich für die Jagd nach dem Programmierer nach unten und ihn zu verletzen, weil es bedeutet, dass ich kann nicht Arrays.asList(), Collections.singletonList() oder Collections.EMPTY_LIST.

verwenden

Leider andere Meinung zu sein, aber ich denke, die Grundregel lautet wie folgt:

  • input Argumente verwenden die generic .
  • Ausgang Werte, die spezifische .

Also, in diesem Fall, dass Sie die Umsetzung als erklären:

public ArrayList<String> foo() {
  return new ArrayList<String>();
}

Begründung : Der Eingangs Fall ist bereits bekannt und erklärt von allen: Verwenden Sie die Schnittstelle, Punkt. Jedoch kann die Ausgabe Fall aussieht kontraintuitiv. Sie möchten die Implementierung zurückkehren, weil der Client möchte über die meisten Informationen haben, was empfängt. In diesem Fall mehr Wissen ist Macht .

Beispiel 1: Der Kunde will das fünfte Element bekommen:

  • zurück Collection: bis zum 5. Element vs Rückkehr Liste iterieren muss:
  • zurück Liste: list.get(4)

Beispiel 2: Der Kunde will das fünfte Element entfernen:

  • Rückkehr Liste:. Muss eine neue Liste ohne das angegebene Element erstellen (list.remove() ist optional)
  • zurück Arraylist: arrayList.remove(4)

So ist es eine große Wahrheit ist, dass Schnittstellen groß ist, weil es die Wiederverwendbarkeit fördert, reduziert Kopplung, verbessert die Wartbarkeit und macht glücklich ... aber nur, wenn sie als input .

Also, noch einmal, kann die Regel wie folgt ausgedrückt werden:

  • Seien Sie flexibel für das, was Sie anbieten.
  • Seien Sie informativ mit dem, was Sie liefern.

Also, das nächste Mal bitte die Implementierung zurück.

Sie verwenden Schnittstelle zu abstrahieren, weg von der tatsächlichen Umsetzung. Die Schnittstelle ist im Grunde nur ein Plan für das, was Ihre Implementierung tun können.

Schnittstellen sind ein gutes Design, weil sie Ihnen Details der Implementierung zu ändern, ohne befürchten zu müssen, dass eine ihrer Verbraucher direkt betroffen sind, solange Sie noch Implementierung tut, was Ihre Schnittstelle sagt, es tut.

Um mit Schnittstellen arbeiten Sie würde sie wie folgt instanziiert:

IParser parser = new Parser();

Jetzt IParser wäre Ihre Schnittstelle und Parser wäre Ihre Implementierung. Nun, wenn Sie von oben mit dem Parser-Objekt arbeiten, arbeiten Sie an der Schnittstelle (IParser), die wiederum gegen die Implementierung arbeiten (Parser).

Das bedeutet, dass Sie das Innenleben der Parser so viel ändern können, wie Sie wollen, wird es nie Code beeinflussen, die Ihre IParser Parser-Schnittstelle arbeitet gegen.

In der allgemeinen Gebrauch die Schnittstelle in allen Fällen, wenn Sie haben keine Notwendigkeit, von der Funktionalität der konkreten Klasse. Beachten Sie, dass für Listen, Java hat einen Random Marker Klasse in erster Linie einen gemeinsamen Fall zu unterscheiden, wo ein Algorithmus müssen wissen, ob get (i) konstante Zeit ist oder nicht.

Für Anwendungen von Code, über Michael ist richtig, dass wie möglich in die Methodenparameter als Gattungswesen ist oft sogar noch wichtiger. Dies gilt insbesondere, wenn ein solches Verfahren zu testen.

Sie finden (oder gefunden haben), dass, wie Sie Schnittstellen zurückgeben, sie durch den Code durchdringen. z.B. Sie geben eine Schnittstelle von Methode A und Sie wird , um dann eine Schnittstelle zum Verfahren B übergeben.

Was Sie tun, ist die Programmierung durch Vertrag, wenn auch in eingeschränkter Weise.

Dies gibt Ihnen enorme Möglichkeiten Implementierungen unter der Decke (diese neuen Objekte erfüllen die bestehenden Verträge / erwartetes Verhalten zur Verfügung gestellt) zu ändern.

all dies gegeben, haben Sie Vorteile in Bezug auf die Implementierung der Wahl, und wie können Sie Verhaltensweisen ersetzen (einschließlich Tests - mit spöttischen, zum Beispiel). Falls Sie nicht erraten, ich bin alle für diese und versuchen, zu reduzieren (oder einführen) Schnittstellen, wo immer möglich.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top