it-swarm.com.de

"Programmieren auf eine Schnittstelle" verstehen

Ich bin oft auf den Begriff "Programmieren auf eine Schnittstelle statt auf eine Implementierung" gestoßen, und ich glaube, 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 nach Möglichkeit auf eine abstraktere Ebene einer Klasse (eine Schnittstelle, eine abstrakte Klasse oder manchmal eine Superklasse) beziehen sollte, anstatt sich auf eine konkrete Implementierung zu beziehen.

Ein häufiges Beispiel in Java ist die Verwendung von:

List myList = new ArrayList(); anstelle von ArrayList myList = new ArrayList();.

Ich habe zwei Fragen dazu:

  1. Ich möchte sicherstellen, dass ich die Hauptvorteile dieses Ansatzes verstehe. Ich denke, die Vorteile sind hauptsächlich Flexibilität. Das Deklarieren eines Objekts als Referenz auf höherer Ebene und nicht als konkrete 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?

  2. Gibt es mehr Möglichkeiten, auf eine Schnittstelle zu programmieren? Oder ist "Deklarieren einer Variablen als Schnittstelle statt als konkrete Implementierung" die einzige Implementierung dieses Konzepts?

Ich spreche nicht über die Java Konstruktschnittstelle. Ich spreche über das OO Prinzip "Programmierung auf eine Schnittstelle, keine Implementierung ". In diesem Prinzip bezieht sich die Welt" Schnittstelle "bezieht sich auf einen beliebigen" Supertyp "einer Klasse - eine Schnittstelle, eine abstrakte Klasse oder eine einfache Oberklasse, die abstrakter und weniger konkret ist als Es sind konkretere Unterklassen.

32
Aviv Cohn

"Programmieren auf eine Schnittstelle" bedeutet, dass man sich nach Möglichkeit auf eine abstraktere Ebene einer Klasse (eine Schnittstelle, eine abstrakte Klasse oder manchmal eine Superklasse) beziehen sollte, anstatt sich auf eine konkrete Implementierung zu beziehen.

Dies ist nicht korrekt. Oder zumindest ist es nicht ganz richtig.

Der wichtigere Punkt kommt aus Sicht des Programmdesigns. Hier bedeutet "Programmieren auf eine Schnittstelle", dass Sie Ihr Design auf was den Code konzentrieren, nicht auf wie es tut es. 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 Software, mit der Sie Ihre Einkaufsliste verfolgen können. In den 80er Jahren arbeitete diese Software gegen eine Befehlszeile und einige Flatfiles auf Diskette. Dann hast du eine Benutzeroberfläche. Dann legen Sie vielleicht die Liste in die Datenbank. Später wurde es möglicherweise in die Cloud oder auf Mobiltelefone oder in die Facebook-Integration verschoben.

Wenn Sie Ihren Code speziell für die Implementierung entworfen haben (Disketten und Befehlszeilen), wären Sie auf Änderungen schlecht vorbereitet. Wenn Sie Ihren Code um die Benutzeroberfläche herum entworfen haben (Manipulieren einer Einkaufsliste), kann die Implementierung geändert werden.

50
Telastyn

Mein Verständnis von "Programmieren auf eine Schnittstelle" unterscheidet sich von dem, was die Frage oder die anderen Antworten nahelegen. Das heißt nicht, dass mein Verständnis korrekt 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 eine Schnittstelle bedeutet, dass Sie, wenn Ihnen eine Programmierschnittstelle (sei es eine Klassenbibliothek, eine Reihe von Funktionen, ein Netzwerkprotokoll oder etwas anderes) angezeigt wird, weiterhin nur die von der Schnittstelle garantierten Dinge verwenden. Möglicherweise haben Sie Kenntnisse über die zugrunde liegende Implementierung (Sie haben sie möglicherweise geschrieben), aber Sie sollten diese Kenntnisse niemals verwenden.

Angenommen, die API zeigt Ihnen einen undurchsichtigen Wert an, der ein "Handle" für etwas Internes darstellt. Ihr Wissen könnte Ihnen sagen, dass dieses Handle wirklich ein Zeiger ist, und Sie könnten es dereferenzieren und auf einen Wert zugreifen, wodurch Sie möglicherweise leicht eine Aufgabe ausführen können, die Sie ausführen möchten. Die Schnittstelle bietet Ihnen diese Option jedoch nicht. Es ist Ihr Wissen über die jeweilige Implementierung.

Das Problem dabei ist, dass es eine starke Kopplung zwischen Ihrem Code und der Implementierung schafft, genau das, was die Schnittstelle verhindern sollte. Abhängig von der Politik kann dies entweder 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 immer wieder beschädigt 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 notwendig gewesen wäre. Diese Tricks führten aber auch dazu, dass das Programm unter Windows 2000 abstürzte, da die API dort anders implementiert war. Wenn das Programm wichtig genug wäre, könnte Microsoft tatsächlich einen Hack zu ihrer Implementierung hinzufügen, damit das Programm weiterhin funktioniert, aber die Kosten dafür sind eine erhöhte Komplexität (mit allen sich daraus ergebenden Problemen) des Windows-Codes. Es macht den Weinleuten auch das Leben besonders schwer, weil sie versuchen, die WinAPI ebenfalls zu implementieren, aber sie können sich nur auf die Dokumentation beziehen, um dies zu tun, was dazu führt, dass viele Programme nicht so funktionieren, wie sie sollten, weil sie (aus Versehen) oder absichtlich) verlassen Sie sich auf einige Implementierungsdetails.

19
Sebastian Redl

Ich kann nur über meine persönlichen Erfahrungen sprechen, da mir dies auch nie offiziell beigebracht wurde.

Ihr erster Punkt ist richtig. Die gewonnene Flexibilität ergibt sich aus der Möglichkeit, nicht versehentlich Implementierungsdetails der konkreten Klasse aufzurufen, in denen sie nicht aufgerufen werden sollten.

Betrachten Sie beispielsweise eine ILogger -Schnittstelle, die derzeit als konkrete LogToEmailLogger -Klasse implementiert ist. Die Klasse LogToEmailLogger macht alle Methoden und Eigenschaften von ILogger verfügbar, verfügt jedoch auch über eine implementierungsspezifische Eigenschaft sysAdminEmail.

Wenn Ihr Logger in Ihrer Anwendung verwendet wird, sollte es nicht das Problem des konsumierenden Codes sein, das sysAdminEmail festzulegen. Diese Eigenschaft sollte während des Logger-Setups festgelegt und vor der Welt verborgen werden.

Wenn Sie für die konkrete Implementierung codiert haben, haben Sie möglicherweise versehentlich die Implementierungseigenschaft festgelegt, wenn Sie den Logger verwenden. Jetzt ist Ihr Anwendungscode eng mit Ihrem Logger verbunden. Wenn Sie zu einem anderen Logger wechseln, müssen Sie zuerst Ihren Code vom ursprünglichen Code entkoppeln.

In diesem Sinne Codierung an eine Schnittstelle lockert die Kopplung.

Zu Ihrem zweiten Punkt: Ein weiterer Grund, den ich für die Codierung einer Schnittstelle gesehen habe, besteht darin, die Komplexität des Codes zu verringern.

Stellen Sie sich zum Beispiel vor, ich hätte ein Spiel mit den folgenden Schnittstellen I2DRenderable, I3DRenderable, IUpdateable. Es ist nicht ungewöhnlich, dass einzelne Spielkomponenten sowohl 2D- als auch 3D-Rendering-Inhalte enthalten. Andere Komponenten können nur 2D und andere nur 3D sein.

Wenn das 2D-Rendering von einem Modul ausgeführt wird, ist es sinnvoll, eine Sammlung von I2DRenderable Zu verwalten. Es spielt keine Rolle, ob die Objekte in ihrer Sammlung auch I3DRenderable Oder IUpdateble sind, da andere Module für die Behandlung dieser Aspekte der Objekte verantwortlich sind.

Durch das Speichern der renderbaren Objekte als Liste von I2DRenderable Wird die Komplexität der Rendering-Klasse 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 hält die Codierung in eine Schnittstelle die Komplexität durch Isolieren von Bedenken gering.

9
MetaFight

Es gibt möglicherweise zwei Verwendungszwecke der hier verwendeten Word-Oberfläche. Die Schnittstelle, auf die Sie sich in Ihrer Frage hauptsächlich beziehen, ist eine Java-Schnittstelle . Das ist speziell ein Java Konzept, im Allgemeinen ist es eine Programmiersprachenschnittstelle.

Ich würde sagen, dass das Programmieren auf eine Schnittstelle ein umfassenderes Konzept ist. Die jetzt beliebten REST APIs, die für viele Websites verfügbar sind, sind ein weiteres Beispiel für das umfassendere Konzept der Programmierung auf einer Schnittstelle auf einer höheren Ebene. Indem Sie eine Ebene zwischen dem Innenleben und dem Äußeren Ihres Codes erstellen Welt (Leute im Internet, andere Programme, sogar andere Teile desselben Programms) Sie können alles in Ihrem Code ändern, solange Sie nicht ändern, was die Außenwelt erwartet, wo dies durch eine Schnittstelle oder einen Vertrag definiert ist dass du vorhast zu ehren.

Dies gibt Ihnen dann die Flexibilität, Ihren internen Code umzugestalten, ohne alle anderen Dinge mitteilen zu 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 Personen nicht beschädigen. Wenn Sie die Benutzeroberfläche wirklich ändern müssen, können Sie eine neue Hauptversion (1.a.b.c bis 2.x.y.z) der API veröffentlichen, die signalisiert, dass Änderungen an der Benutzeroberfläche der neuen Version fehlerhaft sind.

Wie @Doval in den Kommentaren zu dieser Antwort hervorhebt, gibt es auch eine Lokalität von Fehlern. Ich denke, das alles läuft auf die Einkapselung hinaus. So wie Sie es für Objekte in einem objektorientierten Design verwenden würden, ist dieses Konzept auch auf einer höheren Ebene nützlich.

4
Encaitar

Eine reale Analogie könnte helfen:

Ein Netzstecker in einer Schnittstelle.
Ja; das dreipolige Ding am Ende des Netzkabels von Ihrem Fernseher, Radio, Staubsauger, Waschmaschine usw.

Jedes Gerät mit einem Hauptstecker (d. H. Implementiert die Schnittstelle "Hat einen Netzstecker") kann in gena auf die gleiche Weise behandelt werden. Sie können alle an eine Steckdose angeschlossen werden und Strom aus dieser Steckdose beziehen.

Was jedes einzelne Gerät tut ist völlig anders. Sie werden nicht weit kommen, um Ihre Teppiche mit dem Fernseher zu reinigen, und die meisten Leute schauen nicht zur Unterhaltung auf ihre Waschmaschine. Alle diese Geräte haben jedoch das gemeinsame Verhalten, an eine Steckdose angeschlossen werden zu können.

Das ist es, was Interfaces Ihnen bringen.
nified Verhaltensweisen, die von vielen verschiedenen Objektklassen ausgeführt werden können, ohne dass die Komplikationen der Vererbung erforderlich sind.

4
Phill W.

Der Begriff "Programmierung auf eine Schnittstelle" kann viel interpretiert werden. Schnittstelle in der Softwareentwicklung ist ein sehr verbreitetes Wort. So erkläre ich das Konzept den Nachwuchsentwicklern, die ich im Laufe der Jahre geschult habe.

In der Softwarearchitektur gibt es eine Vielzahl natürlicher Grenzen. Häufige Beispiele sind

  • Die Netzwerkgrenze zwischen Client- und Serverprozessen
  • Die API-Grenze zwischen einer Anwendung und einer Bibliothek eines Drittanbieters
  • Interne Code-Grenze zwischen verschiedenen Geschäftsbereichen innerhalb des Programms

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, festgelegt wird. Sie testen Ihre Software nicht daran, ob sich die "andere Seite" verhält, sondern ob Ihre Interaktionen mit der Spezifikation übereinstimmen.

Die Folgen davon sind:

  • Externe Komponenten können ausgetauscht werden, solange sie die Spezifikation implementieren
  • Natürlicher Punkt für Unit-Tests zur Validierung des korrekten Verhaltens
  • Integrationstests werden wichtig - war die Spezifikation nicht eindeutig?
  • Als Entwickler haben Sie eine kleinere Welt der Besorgnis, wenn Sie an einer bestimmten Aufgabe arbeiten

Während sich ein Großteil davon auf Klassen und Schnittstellen beziehen kann, ist es ebenso wichtig zu erkennen, dass es sich auch auf Datenmodelle, Netzwerkprotokolle und allgemeiner auf die Arbeit mit mehreren Entwicklern bezieht

1
Michael Shaw