Sollte ich vor einer Implementierung eine Schnittstellen-API schreiben?

14

Ich habe mich in letzter Zeit mit "organisierterer" Programmierung befasst und gelernt, dass ich auf eine Schnittstelle programmieren sollte, nicht auf eine Implementierung. Wäre es in diesem Sinne besser, ein Projekt in Schnittstellen zu "skizzieren", bevor Sie, wo möglich, die Implementierung dafür schreiben?

Und wenn dies der Fall ist, sollte ich im Fall der Verwendung von Bibliotheken von Drittanbietern (z. B. Lidgren) diese ebenfalls in Schnittstellen einschließen und durch IOC-Container auflösen, oder ist es in Ordnung, sie für die Schnittstellen verfügbar zu machen?

Dan Pantry
quelle
Nach meiner persönlichen Erfahrung ist es gut, zuerst die Architektur zu entwerfen - die Verantwortung jeder Klasse. Sie müssen es nicht aufschreiben, sondern nur darüber nachdenken oder auf Papier skizzieren. Dann geht es um persönliche Vorlieben, aber ich empfehle, zuerst Dokumentkommentare für jede Methode zu schreiben, die Sie zu implementieren beginnen. Wenn Sie Dokumente schreiben, müssen Sie über die Funktionalität nachdenken, bevor Sie mit dem Schreiben von Code beginnen.
Sulthan
Ja, und programmieren Sie die Schnittstellen (oder abstrakten Klassen), bevor Sie sie implementieren. Es hilft dabei, den Nachrichtenfluss vom Client zum Server und umgekehrt "richtig" zu gestalten, bevor Implementierungen blockiert (und in sie investiert) werden. Sehr gute Diashow zu diesem Thema: Wie man eine gute API entwirft und warum es wichtig ist
Marjan Venema

Antworten:

8

Leider werden Sie feststellen, dass dies oft auf persönliche Vorlieben zurückzuführen ist.

Was Sie bisher beschrieben haben, scheint jedoch gut zu sein. Wenn Sie möchten (und ich empfehle es), können Sie den folgenden Ansatz verwenden:

  1. Schreiben Sie Ihr Anwendungsskelett als Interfaces, abstrakte Klassen (Stubbed) und Klassen (auch Stubbed).
  2. Schreiben Sie Ihre Tests gegen diese Schnittstellen und Stubs (sie schlagen vorerst fehl)
  3. Schreiben Sie Ihre Implementierungen (Ihre Tests werden bestanden, sobald Sie Ihre Implementierung abgeschlossen haben).

Sie konzentrieren sich darauf, mehr "organisierten" Code zu schreiben. Das Befolgen von TDD hilft Ihnen dabei.

Einige zusätzliche Punkte:

  • IoC-Container sind praktisch. Benutze sie und DI so oft du kannst.
  • Sie 3rd - Party - Bibliotheken wickeln. Dadurch wird die Kopplung zwischen Ihrem Code (Code, den Sie steuern) und dem Code eines Drittanbieters (Code, den Sie nicht steuern) gelöst.
MetaFight
quelle
1
Dies war, was ich ursprünglich gedacht hatte, aber mir wurde gesagt, es würde das YAGNI-Prinzip verletzen. Das Problem, das ich bei vielen meiner Projekte sehe, die nie fertig werden, ist, dass sie mit der Menge an Blob-Code, die ich geschrieben habe, schnell nicht mehr gewartet werden können, weil ich sie nicht richtig organisiere oder meinen Angriffsplan nicht plane.
Dan Pantry
Welcher Teil würde gegen YAGNI verstoßen?
MetaFight
Binden von Bibliotheken von Drittanbietern.
Dan Pantry
2
Ich denke, es läuft auf Folgendes hinaus: Was sind die Chancen, dass sich die Bibliothek eines Drittanbieters ändert? Wenn es eine Chance von 0% gibt, dann sicher YAGNI. Dies ist jedoch selten der Fall. Auch Ihre 3rd - Party - Libs Verpackung kann Ihren anderer Code leichter zu Unit - Test machen (wenn zum Beispiel könnten Sie nicht die 3rd - Party - Bibliothek verspotten)
MetaFight
1
@DanPantry: Das Einbinden von Bibliotheken von Drittanbietern ist kein Verstoß gegen YAGNI, sondern ein dringend erforderlicher Schutz vor dem Befall von Bibliotheken von Drittanbietern mit Ihrem eigenen Code. Es geht nicht nur darum, eine Bibliothek auszutauschen, sondern wie MetaFight sagt auch darum, Änderungen in neueren Versionen der Bibliothek zu verhindern, die ansonsten Änderungen im gesamten eigenen Code erfordern würden. Durch das Umschließen der Bibliothek (und insbesondere ihrer spezifischen Typen: Klassen, Aufzählungen, Strukturen usw.) isolieren Sie Ihren eigenen Code und haben einen einzigen Punkt, den Sie ändern können, wenn sich die Bibliothek ändert (aus welchem ​​Grund auch immer).
Marjan Venema
13

Ja, Sie sollten eher gegen Schnittstellen als gegen bekannte Implementierungen programmieren, und ja, Sie sollten Schnittstellen zuerst konstruieren, anstatt sie aus Ihrem eigenen Code hervorgehen zu lassen.

Die Gründe für beide Empfehlungen sind weitgehend die gleichen: Bei der Computerprogrammierung geht es hauptsächlich um menschliche Faktoren. Viele finden das überraschend, aber denken Sie daran: Es gibt nahezu unendlich viele verschiedene Möglichkeiten, um dasselbe Computerproblem zu lösen, das gleichermaßen gut funktioniert. Fast alle von ihnen sind für jeden, der sie nicht geschrieben hat (oder in der Tat kurze Zeit später an den Autor), völlig unverständlich.

Daraus folgt, dass es beim guten Software-Engineering hauptsächlich darum geht, den gewünschten Effekt (korrekte Berechnung mit angemessener Effizienz) so zu erzielen, dass später mit dem Quellcode gearbeitet werden kann. Schnittstellen und APIs sind ein wesentlicher Bestandteil dieser Disziplin: Sie ermöglichen es Ihnen, ein Problem auf einer Beschreibungsebene gleichzeitig zu betrachten. Dies ist weitaus einfacher als das gleichzeitige Nachdenken über Geschäftskonsistenzregeln und über die Implementierung verknüpfter Listen. Daher ist es besser, eine solche Trennung von Bedenken zwangsweise durchzusetzen als es dem Client-Programmierer zu ermöglichen, Ihren Code nach Belieben zu verwenden.

Dies ist schwer zu glauben für viele Cowboy-Programmierer, die davon überzeugt sind, dass sie alles verstehen, was sie schreiben, viel besser als durchschnittliche Denker sind und die Komplexität bewältigen können, die "weniger" Programmierern Schwierigkeiten bereitet. Sich der eigenen kognitiven Grenzen nicht bewusst zu sein, ist ein äußerst verbreitetes Phänomen - deshalb sind Best Practices in der Code-Organisation so wichtig (und werden so oft ignoriert).

Um es zu wiederholen: Schnittstellen und API-Barrieren sind größtenteils gut , auch wenn Sie nur mit sich selbst zusammenarbeiten. Was externe Bibliotheken betrifft, sehe ich kein Problem darin, sie zu verwenden, wenn sie eine gut durchdachte API mitbringen, solange Sie nicht damit rechnen, diese Bibliothek gegen eine andere auszutauschen. Andernfalls kann eine Wrapper- oder Antikorruptionsschicht eine sehr gute Idee sein.

Kilian Foth
quelle
Ich finde es gut, wenn es bei SE hauptsächlich darum geht, den gewünschten Effekt so zu erzielen, dass der Quellcode später bearbeitet werden kann. Ich wünschte, ich hätte es bei meinem letzten Job, bei dem ich immer für sauberen Code kämpfte, so gut ausdrücken können!
MetaFight
Gibt es eine Namenskonvention für APIs, die nur Schnittstellen sind, die ich am Ende überall verwenden werde? Wenn ich ein Befehlsmuster ausführe, nenne ich es "commandables"?
Snoop
@StevieV Es gibt verschiedene, zB IBlahimplementiert von Blahoder Blahimplementiert von BlahImpl. Ich mag nicht beide, und neigen dazu , verwenden , Blahum implementiert OralBlah, WrittenBlahoder ASLBlah. Aber wie immer ist es wichtiger, sich an Ihre vorhandene Codebasis und Ihre Erwartungen zu halten, als an einen allgemeinen Standard.
Kilian Foth
4

Warum nicht Test Driven Development / Design (TDD) untersuchen, anstatt nur auf Schnittstellen zu programmieren?

Viele Leute denken, TDD sei eine Testpraxis, aber tatsächlich ist es ein Entwurfsansatz, bei dem Sie Tests darüber informieren lassen, wie Ihr Code über Tests verwendet wird (zunächst über Komponententests, aber auch über Integrationstests).

Das Programmieren an Schnittstellen ist eine wichtige Waffe in Ihrem Toolset, aber wie die meisten Dinge ist es nicht immer die passende Lösung / Technik / Praxis, da es nicht immer benötigt wird. Sie sollten an Schnittstellen programmieren, an denen Sie müssen.

Wenn Sie TDD verwenden, müssen Sie herausfinden, wo solche Schnittstellen wichtig sind und wo es, ehrlich gesagt, keine Rolle spielt. Und am Ende sollten Sie ziemlich viele Unit-Tests in Ihrer Codebasis haben.

Für die Verwendung von Bibliotheken von Drittanbietern empfehle ich dringend, diese gegebenenfalls in Ihre eigenen Abstraktionen zu packen. und nicht die Kunden Ihrer API "wissen" lassen.

Viel Glück!

[edit: habe Megaflights Antwort gesehen - stimme vollkommen zu]

Rupjones
quelle
2
Bei TDD wird implizit eher an die Schnittstelle als an die Implementierung gedacht, obwohl es sich möglicherweise nicht um eine formale "Schnittstellen" -Deklaration handelt.
DougM
1
Das ist eine großartige Antwort. +1 für das Vorschlagen von TDD, was meiner Meinung nach die Lösung für das eigentliche Problem des OP ist , wo man anfangen soll, wenn man an einem neuen Projekt arbeitet, und ich würde noch einmal +1 geben, wenn ich es für "TDD verwenden" könnte, um herauszufinden, wo solche Schnittstellen sind wichtig sind und wo es, ehrlich gesagt, keine Rolle spielt. "
Benjamin Hodgson
2

Ich denke, es ist übertrieben. Wenn der Benutzer Ihrer API nicht gezwungen werden muss, etwas auf eine bestimmte Weise zu implementieren / zu verwenden, würde ich es weglassen. Schnittstellen sind Verträge. Wenn ich sie nicht benötige, warum sollte ich dann einen geben?

Ich denke, die Leute überbeanspruchen Schnittstellen. Sie fügen eine Komplexitätsebene hinzu, die in den meisten Fällen nicht benötigt wird.

Kyle Johnson
quelle
Ich denke, die Menschen unter -use Schnittstellen. Wenn Sie wiederverwendbare Teile erstellen möchten, ist die Benutzeroberfläche nicht nur eine schöne Ergänzung, sondern die Hauptsache, um die Sie sich kümmern müssen. Neben der eigentlichen Umsetzung natürlich.
JensG
1

Programmieren gegen einen Vertrag ist fast immer eine gute Idee. Dieser Vertrag muss keine Schnittstelle sein, sondern kann von einer Klasse erfüllt werden. Meiner Meinung nach sind die Schnittstellen zusammen mit DI aufgrund von Bedenken in Bezug auf Komponententests und Spott-Frameworks etwas überlastet.

Ich persönlich bevorzuge es, Schnittstellen nur dann einzubringen, wenn ich höchstwahrscheinlich mehr als eine Implementierung eines Vertrags haben oder haben könnte. Schnittstellen eignen sich hervorragend für Repositorys, in denen ich den Datenzugriff abstrahieren möchte, aber wahrscheinlich weniger für meine Standard-Geschäftslogik, die wahrscheinlich relativ unflexibel ist.

Das Fehlen einer Schnittstelle kann insbesondere Puristen Probleme beim Testen von Einheiten bereiten. Aber ich bin daran interessiert, die externen Abhängigkeiten meiner Programme zu verspotten, nicht die internen Abhängigkeiten. Ich möchte, dass meine Tests eine Validierung des Codes durchführen und nicht die Codestruktur wiedergeben.

Peter Smith
quelle