Schnittstellen überall? - Best Practices

Es gibt eine Diskussion über die korrekte Verwendung von Schnittstellen in OOP. Mir wurde beigebracht, und ich habe immer von der Prämisse aus gearbeitet, dass Schnittstellen Konkretionen vorangehen und dass alle Methoden in Verträgen behandelt werden sollten. Das ist für mich die Entkopplung 101.

Ich habe herausgefunden, dass das Anwenden dieses Musters Junior Devs universell die Seile lehrt, mich auf der Straße zum Erfolg macht ("Oh! All dies ist eng miteinander verbunden! Ich kann nichts davon verwenden!") Und braucht sehr wenig Zeit . Es ist einfach (und lohnenswert) zu verstehen, dass wir immer mit Verträgen umgehen.

Ein anderes Mitglied sagt, dass Abstraktion nur angewendet werden sollte, wenn mehrere Implementierungen vorhanden sind, und dass die ganze Zeit für das Team verwirrend ist. Für mich ist es mir egal, es ist nur verwirrend und wird schnell zur zweiten Natur, und für mich ist es die richtige Art zu bauen.

Aber ich wollte herausfinden, ob jemand Referenzen/Expertentexte liefern kann, die erklären, warum oder warum nicht.

2
Ich denke, "Abstraktion" hat mehrere verschiedene Bedeutungen im Software-Engineering. Schnittstellen haben für mich nichts mit Abstraktionen zu tun. Abstraktionen haben nichts mit Implementierungsdetails zu tun. Es hat mit Design zu tun. Konzeptualisierung. Etwas, das Sie mit oder ohne Schnittstellen erreichen könnten.
hinzugefügt der Autor glacier, Quelle
Die Hauptgefahr beim Erstellen einer Schnittstelle aus einer einzelnen Klasse besteht darin, dass Sie es schrecklich falsch verstehen. Stellen Sie sich vor, ein uralter Australier hat die Schnittstelle für IMammal definiert. Sie hätten alle eine erforderliche sizeOfPouch() -Methode ...
hinzugefügt der Autor MrG, Quelle
Erklären Sie, warum Y genau von einer abstrakten Klasse profitiert. Die Abstraktion ist identisch richtig? Kein Code ändert sich. Es gibt Konstruktoraufrufe an Millionen von Orten, wobei zu berücksichtigen ist, dass Klassen keine Konstruktoren bereitstellen müssen. Abhängigkeitsinjektion ist viel älter als C# und Java.
hinzugefügt der Autor Frank Hileman, Quelle
Ich sehe deine Logik nicht. Wenn die API identisch ist, ist der Swap nahtlos, unabhängig von der Verwendung einer Schnittstelle, einer konkreten Klasse oder einer abstrakten Klasse. Der einzige Unterschied ist die Konstruktion, die auf viele verschiedene Arten isoliert werden kann, nicht nur über DI.
hinzugefügt der Autor Frank Hileman, Quelle
InterfaceB bietet nur dann einen Wert, wenn Sie beide Dienste gleichzeitig (Mehrfachvererbung) in demselben Prozess verwenden. Sie können diesen Vorteil jedoch auch durch die Verwendung einer Basisklasse erzielen.
hinzugefügt der Autor Frank Hileman, Quelle
Wenn Sie einen Typ durch einen anderen ersetzen, verwenden Sie entweder eine Basisklasse oder ändern den ursprünglichen Typ. Jede OO-Sprache würde funktionieren. Sie beschreiben eine Situation, in der Sie entweder beide Typen gleichzeitig benötigen oder es unterschiedliche Komponenten gibt, die Komponenten erstellen (Plug-Ins).
hinzugefügt der Autor Frank Hileman, Quelle
Sie haben keine Erklärung für diese Behauptung abgegeben. Ich sage Ihnen aus Anwendersicht, der einzige Unterschied zwischen Schnittstelle (spezialisierte Klasse) und Klasse ist der Konstruktor. Sie müssten ein Beispiel geben, wo dies Millionen kostet, um es zu ändern.
hinzugefügt der Autor Frank Hileman, Quelle
Im Allgemeinen handelt es sich bei teuren Änderungen nicht um Namensänderungen von Typen, sondern um invasive Änderungen an APIs. DI und Schnittstellen helfen nicht weiter. Ihr Szenario steht nicht im Zusammenhang mit dem Speichern von Arbeit.
hinzugefügt der Autor Frank Hileman, Quelle
Ich habe viel Zeit damit verbracht, überflüssige Schnittstellen zu entfernen, weshalb ich ihn als Cargo-Kult-Design bezeichne. Der einzige Vorteil besteht darin, die Kosten für die Entwicklung von Software zu erhöhen. Dies ist möglicherweise für Auftragnehmer und Mitarbeiter von Vorteil, nicht aber für die für die Entwicklung bezahlten Personen.
hinzugefügt der Autor Frank Hileman, Quelle
@froodley Richtig, aber eine Klasse in solchen Sprachen ist sowohl eine Abstraktion (die bereitgestellten Operationen) als auch eine Implementierung in einem Textdokument. Das heißt Während sie in anderen Sprachen getrennt sind, können Sie sie in einer Einheit kombinieren. Sie können immer noch getrennt werden ... eine Klasse kann Mitglieder unimplementiert lassen; Wenn dies der Fall ist, entspricht es einer Schnittstelle ohne die Vorteile der Mehrfachvererbung.
hinzugefügt der Autor Frank Hileman, Quelle
Klassen in der Art von Sprachen, die Sie implizieren, sind sowohl Abstraktionen als auch Implementierungen dieser Abstraktion. Klassen bieten eine "Schnittstelle" (API). Mit dem Schnittstellenschlüsselwort können Sie eine Klasse ohne Implementierung angeben, um Sprachbeschränkungen zu umgehen. Aus Sicht des Benutzers ist weder eine Klasse noch eine Schnittstelle besser oder abstrakter als die andere. Schnittstellen können den Aufbau erschweren.
hinzugefügt der Autor Frank Hileman, Quelle
Es scheint, dass Sie nicht dieselbe Terminologie verwenden, die in der Informatik im Allgemeinen verwendet wird. Klassen sind Abstraktionen, selbst ganze Zahlen und Gleitkommazahlen sind Abstraktionen. Schnittstellen erhöhen nicht die "Abstraktion": Ein abstrakterer Datentyp ist allgemeiner und in der Typhierarchie höher; Der Implementierungsstandort ist aus Abstraktionsperspektive irrelevant.
hinzugefügt der Autor Frank Hileman, Quelle
@Meo Ich stimme zu, Schnittstellen in solchen Sprachen dienen nur der Mehrfachvererbung. Aber ich mag den Missbrauch von "Abstraktion" in der Frage nicht.
hinzugefügt der Autor Frank Hileman, Quelle
In Bezug auf "Entkopplung 101" in dem ursprünglichen Beitrag entkoppelt eine Schnittstelle, die zu den öffentlichen Mitgliedern einer Klasse 1: 1 passt, nichts, sowohl weil es nur eine Implementierung gibt (dh immer noch an eine einzelne Klasse gekoppelt ist) als auch Sie müssen beide gleichzeitig aktualisieren, wenn Sie die öffentlichen Signaturen ändern. Gleichzeitig hat es keinen Vorteil, etwas zu entkoppeln, was gekuppelt werden sollte - Entkopplung gilt für Situationen, in denen die Kopplung ein Problem darstellt. Überflüssige Schnittstellen sind eine Form des Cargo-Kult-Designs.
hinzugefügt der Autor Frank Hileman, Quelle
@froodley Der Konstruktor kann nur von einer Klasse bereitgestellt werden, aber die Klasse muss auch keinen bereitstellen. Ist eine Klasse ohne öffentlichen Konstruktor "Beton"? Benutzern ist es egal, wo die Implementierung liegt. Sie arbeiten nur mit der Abstraktion.
hinzugefügt der Autor Frank Hileman, Quelle
@froodley Was meinst du mit "Beton"? Die Begriffe in den Bereichen Informatik, abstrakter Datentyp und Abstraktion haben nichts mit dem Schlüsselwort "abstract" in Java und C# zu tun. Das Schlüsselwort wird besser als "nicht implementiert" oder im Fall einer Klasse als "unvollständig" bezeichnet. Meinen Sie einen Datentyp, der einen Konstruktor bereitstellt? Eine Abstraktion ist formal eher eine Menge von Operationen sowie die Semantik dieser Operationen. In den Sprachen, von denen wir sprechen, können wir in der Dokumentation nur Semantik angeben. Alle Datentypen sind Abstraktionen - die Implementierung wird von den Benutzern nicht berücksichtigt.
hinzugefügt der Autor Frank Hileman, Quelle
Verwechseln Sie "Schnittstelle" und "Konkretion" in Bezug auf Software-Design mit "Schnittstelle" und "Klasse" in Java/C #? Sie können eine Schnittstelle programmieren, ohne eine eigene Java-Schnittstelle zu schreiben.
hinzugefügt der Autor immibis, Quelle
Ich stimme nicht zu, dass eine konkrete Klasse abstrakt ist. Meinen Sie damit, dass es sich um einen Computercode handelt und nicht wirklich ein Auto? Ich beziehe mich auf die Abstraktion in dem Sinne, dass ich mich mit etwas rein abstrakt beschäftige, als eine Menge von Feldern und Operationen, die es haben sollte, eine vereinbarte Art, über den Gegenstand zu sprechen, der nicht davon abhängt, eine bestimmte Implementierung zu erhalten. Alles außer einer rein abstrakten Klasse ist keine Abstraktion mehr, es sei denn, es handelt sich nicht um ein Auto.
hinzugefügt der Autor Dave Lau, Quelle
Eine Abstraktion kann per Definition keine Implementierung sein.
hinzugefügt der Autor Dave Lau, Quelle
Alternative: Service X wird direkt überall eingespritzt. Es funktioniert überhaupt nicht mehr. Jeder nachgelagerte Verbraucher muss geändert werden, einige in anderen Unternehmen, andere in Software, die von Entwicklern entwickelt wurde, die das Unternehmen verlassen haben.
hinzugefügt der Autor Dave Lau, Quelle
Wenn Sie jemals mehrere Jahre verbracht haben und Millionen Menschen die YAGNI-Slop-Programmierung von jemandem ansprechen, werden Sie vielleicht sehen, warum ich nicht zustimme.
hinzugefügt der Autor Dave Lau, Quelle
Was den Frachtkult angeht, stimme ich wirklich nicht zu, dass er keinem Zweck dient. Ich glaube, dass Sie sich eher mit Abstraktionen als mit spezifischen Implementierungen befassen, und wenn Sie sagen, URLs konfigurierbar zu machen, anstatt sie hart zu codieren, ist Ladungskult, weil sie sich wahrscheinlich nicht ändern werden.
hinzugefügt der Autor Dave Lau, Quelle
Wenn Sie jemals am falschen Ende waren, wenn Ihre Software als eng gekoppelt erklärt wurde, und die Entkopplung x Millionen kosten wird, werden Sie verstehen, dass ich mich weigere, eng gekoppelte Software zu entwickeln. Der Umgang mit Schnittstellen entkoppelt Ihre Software definitiv selbst mit nur einer Implementierung. Jeder Verbraucher kann Ihre Bibliothek durch eine andere ersetzen, die eine Implementierung oder Fassade über seine eigene Implementierung bereitstellt, die dieselbe Schnittstelle bietet. Ich habe wirklich keine Ahnung, woher du kommst. es scheint mir alles nach Faulheit und Schrott zu sein.
hinzugefügt der Autor Dave Lau, Quelle
Alles in einem Computer ist eine Abstraktion.
hinzugefügt der Autor Dave Lau, Quelle
Eine Schnittstelle ist per Definition abstrakter als eine konkrete Klasse.
hinzugefügt der Autor Dave Lau, Quelle
Es ist eher eine konzeptuelle Repräsentation eines Objekts als eine spezifische Implementierung dieser konzeptuellen Repräsentation.
hinzugefügt der Autor Dave Lau, Quelle
Wenn ich sage, dass Sie mir ein ServiceX-Objekt geben müssen, kann ich Ihnen kein ServiceY-Objekt geben. Der Compiler wird es verhindern. Wenn ich sage, dass Sie mir ein InterfaceB-Objekt geben müssen, können Sie mir eines geben.
hinzugefügt der Autor Dave Lau, Quelle
Es erhöht die Kosten zunächst geringfügig und spart gleichzeitig Millionen, wenn es an der Zeit ist, eine Bibliothek mit Dutzenden von Verbrauchern zu ersetzen.
hinzugefügt der Autor Dave Lau, Quelle
Wenn ich einen Typ durch einen anderen ersetze, ändere ich nichts außer der Fabrik, die ich dem Injektor in der Konfig bereitstelle. Es fasst eine andere Klasse zusammen, die interfaceB implementiert, und es wird überhaupt kein Code geändert.
hinzugefügt der Autor Dave Lau, Quelle
Service X ist veraltet, weil das Backend-System ersetzt wurde. Service Y wurde erstellt, um denselben Zweck zu erfüllen. Wir bieten Dienst Z für den Abhängigkeitsinjektor an, und die Kinder in mehreren Bibliotheken und Apps, die nur einen Dienst erwarten, der die Schnittstelle B erfüllt, sind davon nicht betroffen.
hinzugefügt der Autor Dave Lau, Quelle
In welcher Sprache arbeitest du? Sie können ServiceY nicht in eine Klasse injizieren, die ServiceX in einer stark typisierten Sprache erwartet.
hinzugefügt der Autor Dave Lau, Quelle
Wir bieten Service Y *
hinzugefügt der Autor Dave Lau, Quelle
@FrankHileman genau, OP macht im Grunde 2 Schnittstellen für eine Implementierung, während es das Gegenteil sein sollte - 1 Schnittstelle für 2+ Implementierungen.
hinzugefügt der Autor Michael, Quelle
@froodley in der Sprache arbeitest du?
hinzugefügt der Autor Michael, Quelle

5 Antworten

Ich möchte nicht einmal wissen, ob ich eine Schnittstelle benutze (die Schlüsselwortart). Meine Fahrfunktion nimmt ein Auto. Ob das konkret ist oder nicht, geht ihn nichts an.

Deshalb ärgere ich mich über die ICar -Konvention von C #. Holen Sie sich dieses sinnlose ich -Lärm aus meinem Gesicht. Java ist nicht viel besser. Natürlich kann ich per Konvention eine Schnittstelle, abstrakte Klasse oder konkrete Klasse Car im Quellcode nennen, aber wenn ich von einem zum anderen wechsle, muss ich alles neu kompilieren, das es verwendet !

Ich möchte nur ausdrücken, was drive() im Typsystem benötigt. Ich habe kein Bedürfnis nach drive() , um zu wissen, mit was es spricht, außer zu wissen, dass was immer es ist, es kann zuhören.

Übrigens, wenn Sie gute Tests haben, gibt Ihnen eine Sprache mit Ententippen all dies kostenlos.

Aber da ich diese Sprachen verwenden muss, wie ich sie finde, neige ich dazu, Rollenschnittstellen in ihnen. Was bedeutet, ich schreibe kein Laufwerk (Auto) Ich schreibe Laufwerk (DriverControls driverControls) und was auch immer das Steuern, Beschleunigen und Unterbrechen von Meldungen über den will Das DriverControls -Protokoll ist dafür frei.

Wenn es darum geht, bin ich mit dir. Wenn Sie einer dieser Fanatiker sind, die darauf bestehen, dass jede Klasse wie Car ein ICar -Partner hat, können Sie in einen See springen.

6
hinzugefügt
In Java wird Ihre DriverControls-Schnittstelle im Allgemeinen als Drivable bezeichnet.
hinzugefügt der Autor Alexei, Quelle
@froodley stimmte zu. Das heißt, die eigentliche Debatte sollte sich auf die Schwierigkeiten beziehen, die verursacht werden können, wenn Sie konkrete Klassen zuerst verwenden und sie später in Schnittstellen umgestalten. Es ist schwer dies zu tun und das offene, geschlossene Prinzip zu respektieren. Schlimmer ist, wie leicht es ist, sich über den konkreten Status von Car zu informieren. Ich habe versucht zu versuchen, das abzuwenden, während ich konkret hier bleibe. Es ist nicht hübsch.
hinzugefügt der Autor candied_orange, Quelle
Im Grunde aus dem Grund, den Sie angegeben haben, bin ich. Ich möchte mich nicht mit bestimmten Sachen befassen, die ein Auto genannt werden, das die Dinge auf eine bestimmte Art und Weise erledigt. Für mich ist das nur ein einmaliger Müllcode. Ich möchte mit einem Vertrag in Kontakt treten. Ich weiß, was ich über .drive() und .steer() weiß, und gibt mir einen String zurück, und ich muss nicht wissen, ob es ein Auto oder ein Boot ist.
hinzugefügt der Autor Dave Lau, Quelle

Ihnen fehlt ein großer Teil des Puzzles:

Die Verwendung von Schnittstellen überall macht es schwieriger, durch Ihren Code zu navigieren

Angenommen, Sie haben eine große Codebasis mit Hunderten von Klassen, die im Laufe der Jahre gewachsen sind.

Wenn Sie wissen möchten, was in einer Methode passiert, klicken Sie einfach darauf, und die IDE springt zu ihrem Code. Das heißt, wenn es sich um eine Klasse handelt. Wenn es sich um eine Schnittstelle handelt, springen Sie zur Schnittstelle. Dann müssen Sie herausfinden, welche Klasse instanziiert wird. Dies kann eine Weile dauern, gehen Sie dorthin und gehen Sie in eine andere Methode und landen Sie wieder auf einer Schnittstelle Sie müssen wieder herausfinden, was tatsächlich instanziiert wurde, was eine Weile dauern kann usw.

Grundsätzlich wird das Navigieren im Code aufgrund von überflüssigen Schnittstellen immer frustrierender.

Meine Faustregel: benutze eine Schnittstelle, wenn Sie tatsächlich mehrere verschiedene Implementierungen hinter sich haben, nicht nur deswegen .


BEARBEITEN:

Mein "viel härter" wurde wahrscheinlich übertrieben. Ich hätte einfach "härter" oder "unbequem" schreiben sollen.

Um dies zu veranschaulichen, in Eclipse. Mit einer Methode aus einer Klasse:

  • Strg + Klick springt innerhalb der Methode (1 Klick).

Bei einer Methode aus einer Schnittstelle wäre dies:

  • Klicken Sie bei gedrückter Strg-Taste auf
  • weiter oben
  • Klicken Sie mit der rechten Maustaste auf den Namen der Schnittstelle
  • Explorer für offene Typen
  • Klasse auswählen
  • offene Gliederung
  • gehe zu Methode

Ja, Sie können es mit guter IDE-Unterstützung tun, aber es ist immer noch ziemlich ärgerlich.

3
hinzugefügt
Hi ho! Nimm es leicht, Leute! ;) Mein "viel härter" wurde wahrscheinlich übertrieben. Ich hätte einfach "härter" oder "unbequem" schreiben sollen. Um dies mit einer Klassenmethode in Eclipse zu veranschaulichen, springt Strg + Klick innerhalb der Methode (1 Klick). Bei einer Schnittstelle wäre dies Strg + Klick, Verschieben nach oben, Rechtsklick auf Interace-Namen, Öffnen des Typ-Explorers, Auswählen der Klasse, Öffnen der Gliederung, Wechseln zur Methode. Ja, Sie können es mit IDE-Unterstützung tun, aber es sind immer noch 7 Klicks oder so, was immer noch ziemlich ärgerlich ist.
hinzugefügt der Autor TLS, Quelle
@froodley: IMHO, systematisches Hinzufügen von Schnittstellen fügt nur nutzloses Rauschen hinzu. Es verbessert weder die Entkopplung noch stellt es sicher, dass die zugrunde liegende Implementierung den Vertrag respektiert. Es fügt nur unnötige Umwege hinzu, Lärm und nervt Entwickler. Es gibt viele Fälle, in denen Schnittstellen nützlich sind, aber die systematische Anwendung von IMHO ist einfach dumm.
hinzugefügt der Autor TLS, Quelle
@froodley Egal, ob die lib Schnittstellen oder Klassen verwendet, ich halte mich an diese. Es ist mir egal Wenn ich myLib.doThat (someArg) anrufe, ist es egal, ob myLib eine Schnittstelle oder eine Klasse ist. Wenn ich wirklich etwas unabhängig davon bleiben würde, müsste ich einen Wrapper darum bauen. In den meisten Fällen ist das Hinzufügen solcher Abstraktionen jedoch eher hinderlich als nützlich. Es ist ziemlich selten, Abhängigkeiten zu wechseln, und wenn Sie dies tun, werden Sie normalerweise bemerken, dass Ihr Wrapper nicht gut in die andere lib passt, was zu Änderungen an zwei Stellen führt.
hinzugefügt der Autor TLS, Quelle
@froodley Eine Abhängigkeit ersetzen: einmal alle paar Jahre. Den Code navigieren/verstehen/überarbeiten: jeden Tag. Platzieren Sie Schnittstellen überall, wenn Sie es mögen, es ist mir egal, aber behalten Sie Ihre Sachen auf lange Sicht.
hinzugefügt der Autor TLS, Quelle
@Meo wir reden hier nicht über Runable . Dies ist ein fehlerhaftes Beispiel, ähnlich wie "Was ist, wenn Sie eine bestimmte Unterklasse von Object suchen" möchten. Bei der Suche nach einer bestimmten Implementierung von MyInterface gibt es keine Probleme. Es sei denn, Sie haben sich entschieden, alle Ihre benutzerdefinierten Klassen ohne Angabe von MyInterface zu implementieren, aber es gibt größere Probleme.
hinzugefügt der Autor Kayaman, Quelle
@Meo Es wäre nur in einer Situation realistisch, in der die Regel lautet: "Jede Klasse muss eine Schnittstelle implementieren", ohne dass dabei eine welche -Schnittstelle erforderlich ist. Ich sehe das in einem professionellen Umfeld nicht sehr wahrscheinlich. Ich meine, wenn wir in einer feindlichen Umgebung arbeiten, nützen die Schnittstellen nichts, da jemand wissentlich oder unwissentlich falsche Implementierungen schreiben kann. Bedeutet das, dass Schnittstellen unbrauchbar sind, weil sie nicht garantieren, dass die Implementierungen korrekt sind?
hinzugefügt der Autor Kayaman, Quelle
Jede IDE, die es wert ist, kann zu Implementierungen springen. Dies ist in keiner Weise ein gültiger Punkt, es sei denn, Sie verwenden schlechte Werkzeuge.
hinzugefügt der Autor Kayaman, Quelle
@Meo Ich befürworte in keiner Weise, dass jede Klasse eine Schnittstelle implementieren muss. Jedem Nachwuchsentwickler sollte klar sein, welche Art von Klassen eine Schnittstelle (wie z. B. Services) und welche nicht (z. B. Entitäten, Wertobjekte) erfordern. Wenn es keine Architektur oder Richtlinien gibt, spielt es keine Rolle, da die Codebase ohnehin in schlechtem Zustand wäre.
hinzugefügt der Autor Kayaman, Quelle
@Meo Ich denke, der Beitrag wurde nach meinem Kommentar bearbeitet. Ich befürworte keine Schnittstellen für jede Klasse, und ich befürworte keine Architektur, die so schlecht ist, dass Sie nicht verfolgen können, was passiert.
hinzugefügt der Autor Kayaman, Quelle
@Meo stelle mir keine Worte in den Mund und sage dann "Falsch". Ich sage, es gibt keinen Grund, beispielsweise Schnittstellen mit einer einzelnen Implementierung zu vermeiden. Ich sage nicht, dass nicht alle Klassen eine Schnittstelle implementieren, da dies einfach dumm ist (insbesondere, wenn alle Klassen aufgrund von Faulheit Runable implementieren). Ich sage, nutzen Sie Ihre Erfahrung (oder fragen Sie jemanden mit Erfahrung), wenn Sie Designentscheidungen treffen. Ich bin nicht sicher, ob die Kosten, von denen Sie sprechen, Entwicklungs- oder Laufzeitkosten sind, aber das ist in jedem Fall vernachlässigbar. Es ist wie eine Mikrooptimierung durch Vermeidung von Schnittstellen.
hinzugefügt der Autor Kayaman, Quelle
Es ist absolut wichtig. Wenn Sie myLib durch someLib ersetzen können, das die gleiche Signatur implementiert, ohne Ihren Code zu ändern, müssen Sie weniger als 2 Millionen Dollar ausgeben, um die Tausenden von harten Referenzen, die Sie auf myLib gemacht haben, zu ersetzen. Was ich Ihnen gegeben habe, war eine Sprache anstelle einer Maschine, die Sie sind für immer gebunden. Es gibt höhere und härtere und teurere Länder der Entkopplung, um eine noch lockerere Zuordnung zu erreichen. Wenn Sie jedoch nur Abstraktionen verwenden, ist Ihre Software standardmäßig sehr lose gekoppelt und kann ersetzt werden, ohne den Code zu ändern.
hinzugefügt der Autor Dave Lau, Quelle
Es ist schwieriger. Es ist auch schwieriger, Kennwörter nicht fest in die Codebasis zu kodieren oder Geschäftslogik in Ihre Modelle zu integrieren. Es ist streng. Es ist eine "Willst du einen Job oder nicht" für mich.
hinzugefügt der Autor Dave Lau, Quelle
Es ist überhaupt nicht nutzlos. Wenn ich Ihnen eine Klasse oder eine Bibliothek gebe, die sich nur mit Konkretionen befasst, koppeln Sie sich fest an meine Arbeit, und wir sind beide aus dem Kasten gescheitert. Nach meiner Erfahrung ärgert es ungeduldige Nachwuchsentwickler, während erfahrene Entwickler Verträge abschließen und erkennen wollen, dass es sich nicht um Selbstmord handelt.
hinzugefügt der Autor Dave Lau, Quelle
@ Kayaman würden wir sehen, wenn es bearbeitet wurde, war es nicht.
hinzugefügt der Autor Michael, Quelle
@ Kayaman Ich bezog mich auf deinen ersten Kommentar. Die Wahrheit ist, dass "das Navigieren im Code aufgrund von überflüssigen Schnittstellen zunehmend frustrierend wird." und kein Werkzeug kann das ändern. Sie haben geschrieben, dass dies kein gültiger Punkt ist, was falsch ist.
hinzugefügt der Autor Michael, Quelle
@ Kayaman Nein, was Sie sagen, ist das Springen von "überflüssigen Schnittstellen" zur Implementierung oder zurück keine Kosten. Falsch.
hinzugefügt der Autor Michael, Quelle
@ Kayaman Wenn jede Klasse eine eigene Schnittstelle hätte oder die am engsten passende Schnittstelle benötigt würde, dann stimme ich Ihnen zu, dass die IDE damit umgehen sollte. Und wissen Sie, was ist das engste Interface? Die Klasse selbst Die Erwartung, dass keine Navigationskosten durch überlastete Schnittstellen entstehen, ist einfach naiv.
hinzugefügt der Autor Michael, Quelle
@Kayaman Wenn Sie alles übergenerieren, werden Sie unweigerlich vor demselben Problem stehen. Wenn eine Schnittstelle die Norm ist, spart jemand früher oder später die Zeit und verwendet die vorhandene Schnittstelle, wo sie es nicht sollte, und die IDE bietet unlogische Implementierungen an.
hinzugefügt der Autor Michael, Quelle
@Kayaman Wenn Sie schlechte Abstraktionen verwenden, versuchen Sie, diese spezifische Implementierung über die ausführbare Schnittstelle zu finden.
hinzugefügt der Autor Michael, Quelle

Ich stimme deinem Kollegen zu.

Gute Abstraktionen zu schaffen ist schwer. So richtig, wirklich HART!

Abstraktionen machen Software komplexer. Sie machen es schwieriger, durch den Code zu navigieren und darüber nachzudenken. Sie machen es schwieriger, Änderungen vorzunehmen, da Änderungen in Abstraktionen häufig an mehreren Stellen erforderlich sind.

Eine Schnittstelle, die nur die Methoden der Klasse kopiert, ist keine gute Abstraktion. Sie können nur sagen, dass Sie eine gute Abstraktion haben, wenn Sie sich mehrere verschiedene Implementierungen vorstellen können (oder tatsächlich haben).

Wann immer Sie eine Abstraktion erstellen, müssen Sie sich eine Frage stellen: "Ist es wirklich wert, den Code zu komplizieren, damit ich diese Teile entkoppeln kann?"

3
hinzugefügt
Gute Abstraktionen zu erstellen ist schwierig, da sie die Komplexität beseitigen. Schlechte Abstraktionen fügen stattdessen hinzu.
hinzugefügt der Autor Shizam, Quelle
@froodley Wenn Ihre Schnittstelle und Klasse dieselbe Signatur haben, haben Sie außer der Konstruktion nichts anderes entkoppelt.
hinzugefügt der Autor Frank Hileman, Quelle
Es ist schwer, großartige Abstraktionen zu erzeugen. Abstraktionen, die der Entkopplung dienen, sind nicht so schwer. Wenn meine Konsumenten mein Modul oder meine Klasse durch ein anderes Modul oder eine andere Klasse ersetzen können, die dieselbe Signatur liefern kann, habe ich das Gefühl, dass ich die Arbeit richtig gemacht habe und sie sind nicht eng mit dem, was ich getan habe, verbunden.
hinzugefügt der Autor Dave Lau, Quelle

Entkopplung ist keine gute Sache für sich. Es ist nur eine gute Sache, wo Sie es brauchen, und schlecht überall sonst. Wenn Sie eine Schnittstelle an einem Ort verwenden, an dem eine hohe Kohäsion bestehen sollte, machen Sie es falsch.

Jedes Interface hat Kosten und einen Wert. Wenn Sie es nicht in Betracht ziehen und es blind machen, dann machen Sie es einfach falsch.

1
hinzugefügt
@froodley Ein Vertrag ist eine Reihe von Operationen und Semantiken. Eine Schnittstelle ist einfach eine Reihe von Operationen, eine Reihe von Signaturen, die aus Sicht des Benutzers genau einer Klasse ähneln. Soweit ich das beurteilen kann, ist das Vorhandensein eines Konstruktors in einer Klasse das, was Sie "konkret" nennen. Entkopplung bedeutet, dass Sie die API unabhängig von der Implementierung ändern können. Eine Klasse kann dies, eine Schnittstelle dies, aber echte Entkopplung bedeutet die Verwendung mehrerer Schnittstellen oder einer dynamischen Sprache.
hinzugefügt der Autor Frank Hileman, Quelle
@froodley Aus Sicht des Benutzers besteht der einzige Abhängigkeitsunterschied zwischen der so genannten "konkreten" Klasse und einer "Schnittstelle" in der Konstruktorabhängigkeit. Sie können eine Klasse erstellen, die einen Konstruktor bereitstellt. Sie können keine spezialisierten Klassen erstellen, die keinen Konstruktor bereitstellen, z. B. eine Schnittstelle. Eine Schnittstelle und eine Klasse sind ansonsten aus Benutzersicht identisch.
hinzugefügt der Autor Frank Hileman, Quelle
Das ist deine Einstellung, meine ist ganz im Gegenteil. Konzeptionell wird eine hohe Kohäsion innerhalb eines Moduls immer noch erreicht, wenn Sie mit eng verwandten Verträgen arbeiten. Ich bin der Meinung, dass das Erstellen von Verträgen, die ständig auf Verträgen basieren, jegliche Unklarheiten beseitigt und das richtige Denken erzwingt, während gleichzeitig eine äußerst austauschbare Codebasis "billig" entsteht, im Vergleich zu dem Versuch, sich später zu entkoppeln, wenn viele Verbraucher bereits auf Sie angewiesen sind Konkretionen. Aber auch hier bin ich auf der Suche nach Literatur zu diesem Thema.
hinzugefügt der Autor Dave Lau, Quelle
Eine Schnittstelle ist ein Vertrag. Eine Klasse ist eine Maschine, die eine Implementierung dieses Vertrages erzeugt. Diese sind sehr, sehr unterschiedlich. Ich bin überhaupt nicht davon überzeugt, dass der Umgang mit Betonmaschinen dem Vertragsabschluss gleichkommt.
hinzugefügt der Autor Dave Lau, Quelle

Ich programmiere Datengeschäftsanwendungen

Dies passt im Allgemeinen nicht zu der klassischen "echten" OOP, bei der das Objekt sein eigenes Verhalten aufgrund aller Geschäftsregeln implementiert, die vom aktuell verbundenen Benutzer usw. abhängen. Stattdessen habe ich die bekannten anämischen model + -Dienste, da ich nichts gefunden habe, das meinen Bedürfnissen mehr entspricht, abgesehen von einigen unabhängigen technischen Komponenten.

Meine Dienste haben immer Schnittstellen, denn selbst wenn ich eine Implementierung habe, wird es eine Zeit geben, in der ich sie verspotten möchte. Ich abstrahiere ein wenig, aber nicht zu viel, denn zu der Zeit, zu der ich es tue, weiß ich nicht so viel, wie viel ich brauchen könnte und wie ich es richtig machen kann, und meine Zeit ist besser, als diese Zeit zu verschwenden (YAGNI/KISS.) ).

Da ich mit nur einer Implementierung beginne, ist meine Schnittstelle natürlich eine Kopie dessen, was meine Implementierung zeigen muss, aber das ist absichtlich so. Wenn ich eine andere Implementierung haben muss, die von Natur aus zwingt, meine Schnittstelle auf eine allgemeinere umzuwandeln, und ich werde es tun, wenn es sein muss.

Hinweis: Ich benutze für die Schnittstelle meiner Dienste, die ich benutze, keinen regulären Namen (kein "Ich" oder was auch immer), da es den Konsumenten dieser Dienste wirklich egal ist, dass sie Schnittstellen sind, wie @candied_orange

0
hinzugefügt
In Java können Sie ohne Schnittstellen genauso wie mit ihnen (mit Mockito und so) nachahmen, es sei denn, Sie möchten die Klasse natürlich manuell implementieren.
hinzugefügt der Autor Michael, Quelle