Ich bin oft auf den Begriff "Programmieren auf eine Schnittstelle anstatt auf eine Implementierung" gestoßen, und ich denke, ich verstehe irgendwie, was das bedeutet. Aber ich möchte sicherstellen, dass ich die Vorteile und möglichen Implementierungen verstehe.
"Programmieren auf eine Schnittstelle" bedeutet, dass man sich, wenn möglich, auf eine abstraktere Ebene einer Klasse (eine Schnittstelle, eine abstrakte Klasse oder manchmal eine Art Superklasse) beziehen sollte, anstatt auf eine konkrete Implementierung zu verweisen.
Ein gängiges Beispiel in Java ist die Verwendung von:
List myList = new ArrayList();
statt ArrayList myList = new ArrayList();
.
Ich habe dazu zwei Fragen:
Ich möchte sicherstellen, dass ich die Hauptvorteile dieses Ansatzes verstehe. Ich denke, die Vorteile liegen hauptsächlich in der Flexibilität. Das Deklarieren eines Objekts als Referenz auf höherer Ebene anstelle einer konkreten Implementierung ermöglicht mehr Flexibilität und Wartbarkeit während des gesamten Entwicklungszyklus und des gesamten Codes. Ist das richtig? Ist Flexibilität der Hauptvorteil?
Gibt es mehr Möglichkeiten, eine Schnittstelle zu programmieren? Oder ist "eine Variable als Schnittstelle anstatt als konkrete Implementierung deklarieren" die einzige Implementierung dieses Konzepts?
Ich spreche nicht über das Java-Konstrukt-Interface . Ich spreche über das OO-Prinzip "Programmieren auf eine Schnittstelle, keine Implementierung". In diesem Prinzip bezieht sich die Welt "Schnittstelle" auf jeden "Supertyp" einer Klasse - eine Schnittstelle, eine abstrakte Klasse oder eine einfache Superklasse, die abstrakter und weniger konkret ist als konkretere Unterklassen.
quelle
Antworten:
Das ist nicht richtig . Oder zumindest ist es nicht ganz richtig.
Der wichtigere Punkt ergibt sich aus der Perspektive der Programmgestaltung. Hier „Programmierung mit einer Schnittstelle“ bedeutet Fokussierung Ihr Design auf , was der Code tut, nicht , wie sie es tut. Dies ist eine wichtige Unterscheidung, die Ihr Design in Richtung Korrektheit und Flexibilität treibt.
Die Hauptidee ist, dass sich Domänen viel langsamer ändern als Software. Angenommen, Sie haben eine Software, mit der Sie Ihre Einkaufsliste verwalten können. In den 80er Jahren arbeitete diese Software gegen eine Befehlszeile und einige flache Dateien auf einer Diskette. Dann hast du eine Benutzeroberfläche. Dann stellen Sie die Liste vielleicht in die Datenbank. Später ist es vielleicht in die Cloud oder auf Mobiltelefone oder die Facebook-Integration umgezogen.
Wenn Sie Ihren Code speziell für die Implementierung (Disketten und Befehlszeilen) entworfen hätten, wären Sie auf Änderungen schlecht vorbereitet. Wenn Sie Ihren Code um die Benutzeroberfläche herum entworfen haben (Manipulieren einer Einkaufsliste), kann die Implementierung frei geändert werden.
quelle
List myList = new ArrayList()
stattArrayList myList = new ArrayList()
. (Frage steht im nächsten Kommentar)List
/ArrayList
Ist nicht das, was ich überhaupt spreche. Es ist eher so, als ob Sie einEmployee
Objekt bereitstellen und nicht die verknüpften Tabellen, die Sie zum Speichern eines Mitarbeiterdatensatzes verwenden. Oder Sie stellen eine Schnittstelle zum Durchlaufen von Titeln zur Verfügung, ohne sich darum zu kümmern, ob diese Titel gemischt oder auf einer CD gespeichert sind oder aus dem Internet gestreamt werden. Sie sind nur eine Abfolge von Liedern.Mein Verständnis von "Programmieren an einer Schnittstelle" unterscheidet sich von dem, was die Frage oder die anderen Antworten nahelegen. Das heißt nicht, dass mein Verständnis richtig ist oder dass die Dinge in den anderen Antworten keine guten Ideen sind, nur, dass sie nicht das sind, woran ich denke, wenn ich diesen Begriff höre.
Das Programmieren auf einer Schnittstelle bedeutet, dass Sie, wenn Ihnen eine Programmierschnittstelle präsentiert wird (sei es eine Klassenbibliothek, eine Reihe von Funktionen, ein Netzwerkprotokoll oder irgendetwas anderes), weiterhin nur Dinge verwenden, die von der Schnittstelle garantiert werden. Möglicherweise haben Sie Kenntnisse über die zugrunde liegende Implementierung (Sie haben sie möglicherweise geschrieben), sollten diese Kenntnisse jedoch niemals verwenden.
Angenommen, die API bietet Ihnen einen undurchsichtigen Wert, der ein "Handle" für etwas Internes ist. Ihr Wissen könnte Ihnen sagen, dass dieses Handle wirklich ein Zeiger ist, und Sie könnten es dereferenzieren und auf einen Wert zugreifen, der es Ihnen möglicherweise ermöglicht, eine Aufgabe, die Sie erledigen möchten, auf einfache Weise zu erledigen. Die Benutzeroberfläche bietet Ihnen diese Option jedoch nicht. Es ist Ihr Wissen über die jeweilige Implementierung, die dies tut.
Das Problem dabei ist, dass es eine starke Kopplung zwischen Ihrem Code und der Implementierung schafft, genau das, was die Schnittstelle verhindern sollte. Je nach Politik kann dies bedeuten, dass die Implementierung nicht mehr geändert werden kann, da dies Ihren Code beschädigen würde, oder dass Ihr Code sehr zerbrechlich ist und bei jedem Upgrade oder jeder Änderung der zugrunde liegenden Implementierung unterbrochen wird.
Ein großes Beispiel hierfür sind Programme, die für Windows geschrieben wurden. Die WinAPI ist eine Schnittstelle, aber viele Leute verwendeten Tricks, die aufgrund der speziellen Implementierung in Windows 95 funktionierten. Diese Tricks haben ihre Programme möglicherweise schneller gemacht oder es ihnen ermöglicht, Dinge mit weniger Code zu tun, als dies sonst nötig gewesen wäre. Diese Tricks bedeuteten aber auch, dass das Programm unter Windows 2000 abstürzen würde, da die API dort anders implementiert wurde. Wenn das Programm wichtig genug wäre, könnte Microsoft die Implementierung tatsächlich etwas hacken, damit das Programm weiterhin funktioniert, aber die Kosten dafür erhöhen die Komplexität (mit allen daraus resultierenden Problemen) des Windows-Codes. Dies macht den Wine-Leuten auch das Leben besonders schwer, da sie versuchen, auch die WinAPI zu implementieren, sich jedoch nur auf die Dokumentation beziehen können,
quelle
Ich kann nur über meine persönlichen Erfahrungen sprechen, da mir dies auch nie offiziell beigebracht wurde.
Dein erster Punkt ist richtig. Die gewonnene Flexibilität beruht darauf, dass Implementierungsdetails der konkreten Klasse nicht versehentlich aufgerufen werden können, wenn sie nicht aufgerufen werden sollten.
Stellen Sie sich zum Beispiel eine
ILogger
Schnittstelle vor, die derzeit als konkreteLogToEmailLogger
Klasse implementiert ist . DieLogToEmailLogger
Klasse macht alleILogger
Methoden und Eigenschaften verfügbar, verfügt jedoch zufällig über eine implementierungsspezifische EigenschaftsysAdminEmail
.Wenn Ihr Logger in Ihrer Anwendung verwendet wird, sollte es nicht das Problem des konsumierenden Codes sein, den einzurichten
sysAdminEmail
. Diese Eigenschaft sollte während der Einrichtung des Loggers festgelegt und vor der Welt verborgen werden.Wenn Sie gegen die konkrete Implementierung codieren, können Sie die Implementierungseigenschaft versehentlich festlegen, wenn Sie den Logger verwenden. Jetzt ist Ihr Anwendungscode fest mit Ihrem Logger verbunden. Wenn Sie zu einem anderen Logger wechseln, müssen Sie zuerst Ihren Code vom Original entkoppeln.
In diesem Sinne löst die Codierung auf eine Schnittstelle die Kopplung .
Zum zweiten Punkt: Ein weiterer Grund, den ich für das Codieren in eine Schnittstelle gesehen habe, besteht darin, die Komplexität des Codes zu verringern.
Zum Beispiel stelle ich mir ein Spiel mit den folgenden Schnittstellen
I2DRenderable
,I3DRenderable
,IUpdateable
. Es ist nicht ungewöhnlich, dass einzelne Spielkomponenten sowohl 2D- als auch 3D-darstellbare Inhalte enthalten. Andere Komponenten können nur 2D und andere nur 3D sein.Wenn 2D-Rendering von einem Modul ausgeführt wird, ist es sinnvoll, eine Sammlung von
I2DRenderable
s zu verwalten. Es spielt keine Rolle, ob die Objekte in seiner Sammlung auchI3DRenderable
oderIUpdateble
als andere Module für die Behandlung dieser Aspekte der Objekte verantwortlich sind.Durch das Speichern der darstellbaren Objekte als Liste von wird
I2DRenderable
die Komplexität der Darstellungsklasse gering gehalten. Die 3D-Rendering- und Aktualisierungslogik spielt keine Rolle. Daher können und sollten diese Aspekte der untergeordneten Objekte ignoriert werden.In diesem Sinne wird durch das Codieren in eine Schnittstelle die Komplexität gering gehalten, indem Bedenken isoliert werden .
quelle
Es gibt vielleicht zwei Verwendungszwecke des hier verwendeten Wortes Interface. Die Schnittstelle, auf die Sie sich in Ihrer Frage hauptsächlich beziehen, ist eine Java-Schnittstelle . Dies ist speziell ein Java-Konzept, im Allgemeinen eine Programmiersprachenschnittstelle.
Ich würde sagen, dass die Programmierung einer Schnittstelle ein umfassenderes Konzept ist. Die mittlerweile beliebten REST-APIs, die für viele Websites verfügbar sind, sind ein weiteres Beispiel für das umfassendere Konzept der Programmierung einer Schnittstelle auf einer höheren Ebene. Indem Sie eine Ebene zwischen dem Innenleben Ihres Codes und der Außenwelt (Personen im Internet, anderen Programmen, selbst anderen Teilen desselben Programms) erstellen, können Sie alles in Ihrem Code ändern, solange Sie nichts an der Außenwelt ändern erwartet, wo dies durch eine Schnittstelle oder einen Vertrag definiert ist, den Sie einhalten möchten.
Das gibt Ihnen dann die Flexibilität, Ihren internen Code umzugestalten, während Sie nicht alle anderen Dinge erklären müssen, die von seiner Schnittstelle abhängen.
Dies bedeutet auch, dass Ihr Code stabiler sein sollte. Wenn Sie sich an die Benutzeroberfläche halten, sollten Sie den Code anderer Benutzer nicht beschädigen. Wenn Sie die Benutzeroberfläche wirklich ändern müssen, können Sie eine neue Hauptversion (1.abc bis 2.xyz) der API freigeben, die anzeigt, dass Änderungen an der Benutzeroberfläche der neuen Version vorgenommen wurden.
Wie @Doval in den Kommentaren zu dieser Antwort ausführt, gibt es auch Fehlerlokalitäten. Ich denke, dass alles auf die Verkapselung hinausläuft. So wie Sie es für Objekte in einem objektorientierten Entwurf verwenden würden, ist dieses Konzept auch auf einer höheren Ebene nützlich.
quelle
Eine echte Analogie könnte helfen:
Ein Netzstecker in eine Schnittstelle.
Ja; das dreipolige Ding am Ende des Netzkabels von Ihrem Fernseher, Radio, Staubsauger, Waschmaschine usw.
Von Geräten, die Hauptstecker hat (dh die Arbeitsgeräte „hat einen Netzstecker“ interface) kann in behandelt werden genau die gleiche Art und Weise; Sie können alle an eine Wandsteckdose angeschlossen werden und über diese Steckdose Strom beziehen.
Was jedes einzelne Gerät macht, ist völlig anders. Sie werden es nicht weit bringen, Ihre Teppiche mit dem Fernseher zu reinigen, und die meisten Leute schauen nicht auf ihre Waschmaschine, um sich zu unterhalten. Alle diese Geräte haben jedoch das gemeinsame Verhalten, in eine Steckdose eingesteckt zu werden.
Das bekommen Sie mit Interfaces.
Einheitliches Verhalten, das von vielen verschiedenen Objektklassen ausgeführt werden kann , ohne dass die Komplikationen der Vererbung erforderlich sind.
quelle
Der Begriff "Programmierung an einer Schnittstelle" ist vieldeutig. Schnittstelle in der Softwareentwicklung ist ein sehr verbreitetes Wort. So erkläre ich den Junior-Entwicklern, die ich im Laufe der Jahre ausgebildet habe, das Konzept.
In der Software-Architektur gibt es eine Vielzahl natürlicher Grenzen. Allgemeine Beispiele schließen ein
Was zählt, ist, dass, wenn diese natürlichen Grenzen existieren, sie identifiziert werden und der Vertrag darüber, wie sich diese Grenze verhält, spezifiziert wird. Sie testen Ihre Software nicht danach, ob sich die "andere Seite" verhält, sondern ob Ihre Interaktionen mit der Spezifikation übereinstimmen.
Die Konsequenzen daraus sind:
Ein Großteil davon kann sich auf Klassen und Schnittstellen beziehen, es ist jedoch ebenso wichtig zu wissen, dass es sich auch auf Datenmodelle, Netzwerkprotokolle und allgemeiner auf die Arbeit mit mehreren Entwicklern bezieht
quelle