920 Stimmen

Was bedeutet es, "auf eine Schnittstelle zu programmieren"?

Ich habe das schon ein paar Mal gehört und weiß nicht, was es bedeutet. Wann und warum sollten Sie dies tun?

Ich weiß, was Schnittstellen bewirken, aber die Tatsache, dass ich mir darüber nicht im Klaren bin, lässt mich glauben, dass ich sie nicht richtig nutze.

Ist es einfach so, wenn Sie das tun würden:

IInterface classRef = new ObjectWhatever()

Sie können jede Klasse verwenden, die IInterface ? Wann sollten Sie das tun? Das Einzige, was mir einfällt, ist, wenn Sie eine Methode haben und sich nicht sicher sind, welches Objekt außer der Implementierung der Methode übergeben werden soll. IInterface . Ich kann mir nicht vorstellen, wie oft Sie das tun müssen.

Und wie kann man eine Methode schreiben, die ein Objekt aufnimmt, das eine Schnittstelle implementiert? Ist das möglich?

4 Stimmen

Wenn Sie sich daran erinnern können und Ihr Programm optimal sein soll, können Sie kurz vor der Kompilierung die Schnittstellendeklaration gegen die tatsächliche Implementierung austauschen. Die Verwendung einer Schnittstelle fügt eine Ebene der Indirektion hinzu, die zu Leistungseinbußen führt. Verteilen Sie Ihren auf Schnittstellen programmierten Code jedoch...

25 Stimmen

@Ande Turner: Das ist ein schlechter Ratschlag. 1). "Ihr Programm muss optimal sein" ist kein guter Grund für das Auslagern von Schnittstellen! Dann sagen Sie: "Verteilen Sie Ihren programmierten Code trotzdem auf Schnittstellen..." Sie raten also, dass Sie bei Anforderung (1) dann suboptimalen Code veröffentlichen?!?

82 Stimmen

Die meisten der Antworten hier sind nicht ganz richtig. Es bedeutet nicht oder impliziert nicht einmal "das Schlüsselwort interface verwenden". Eine Schnittstelle ist eine Spezifikation, wie etwas zu verwenden ist - gleichbedeutend mit dem Vertrag (schlagen Sie es nach). Getrennt davon ist die Implementierung, die besagt, wie der Vertrag erfüllt wird. Programmieren Sie nur gegen die Garantien der Methode/des Typs, so dass, wenn die Methode/der Typ auf eine Weise geändert wird, die immer noch dem Vertrag entspricht, der Code, der sie verwendet, nicht kaputt geht.

5voto

Wenn ich eine neue Klasse schreibe Swimmer um die Funktionalität hinzuzufügen swim() und müssen ein Objekt der Klasse Dog und diese Dog Klasse implementiert Schnittstelle Animal die besagt, dass swim() .

An der Spitze der Hierarchie ( Animal ), ist es sehr abstrakt, während es unten ( Dog ) ist es sehr konkret. Die Art und Weise, wie ich über "Programmierung auf Schnittstellen" denke, ist, dass ich schreibe Swimmer Klasse, möchte ich meinen Code gegen die Schnittstelle schreiben, die in dieser Hierarchie am weitesten oben steht, was in diesem Fall eine Animal Objekt. Eine Schnittstelle ist frei von Implementierungsdetails und macht Ihren Code somit lose gekoppelt.

Die Implementierungsdetails können im Laufe der Zeit geändert werden, was sich jedoch nicht auf den übrigen Code auswirkt, da Sie nur mit der Schnittstelle und nicht mit der Implementierung interagieren. Es ist Ihnen egal, wie die Implementierung aussieht... Sie wissen nur, dass es eine Klasse geben wird, die die Schnittstelle implementieren wird.

4voto

jaco0646 Punkte 13032

Bei den bisherigen Antworten lag der Schwerpunkt auf der Programmierung auf einer Abstraktion, um die Erweiterbarkeit und die lose Kopplung zu gewährleisten. Dies sind zwar sehr wichtige Punkte, Lesbarkeit ist ebenso wichtig. Die Lesbarkeit ermöglicht es anderen (und Ihrem zukünftigen Ich), den Code mit minimalem Aufwand zu verstehen. Aus diesem Grund nutzt die Lesbarkeit Abstraktionen.

Eine Abstraktion ist per Definition einfacher als ihre Implementierung. Eine Abstraktion lässt Details weg, um das Wesentliche oder den Zweck einer Sache zu vermitteln, aber nicht mehr. Da Abstraktionen einfacher sind, kann ich im Vergleich zu Implementierungen viel mehr von ihnen auf einmal in meinem Kopf unterbringen.

Als Programmierer (in jeder Sprache) habe ich eine allgemeine Vorstellung von einer List die ganze Zeit in meinem Kopf. Vor allem eine List erlaubt den zufälligen Zugriff, die Duplizierung von Elementen und die Aufrechterhaltung der Ordnung. Wenn ich eine Erklärung wie diese sehe: List myList = new ArrayList() Glaube ich, cool ist dies ein List das in der (grundlegenden) Weise verwendet wird, die ich verstehe; und ich muss nicht weiter darüber nachdenken.

Andererseits trage ich nicht die spezifischen Implementierungsdetails von ArrayList in meinem Kopf. Wenn ich also sehe, ArrayList myList = new ArrayList() . denke ich, Oh-oh diese ArrayList muss in einer Weise verwendet werden, die nicht unter die List Schnittstelle. Jetzt muss ich alle Verwendungszwecke dieser Schnittstelle aufspüren ArrayList um zu verstehen, warum, denn sonst kann ich diesen Code nicht vollständig verstehen. Noch verwirrender wird es, wenn ich feststelle, dass 100 % der Verwendungen dieses ArrayList faire entsprechen dem List Schnittstelle. Dann frage ich mich... gab es einen Code, der sich auf ArrayList Implementierungsdetails, die gelöscht wurden? War der Programmierer, der sie instanziiert hat, einfach nur inkompetent? Ist diese Anwendung zur Laufzeit auf irgendeine Weise an diese spezielle Implementierung gebunden? Auf eine Weise, die ich nicht verstehe?

Ich bin jetzt verwirrt und unsicher über diese Anwendung, und alles, worüber wir reden, ist eine einfache List . Was wäre, wenn es sich um ein komplexes Geschäftsobjekt handeln würde, das seine Schnittstelle ignoriert? Dann reicht mein Wissen über die Geschäftsdomäne nicht aus, um den Zweck des Codes zu verstehen.

Selbst wenn ich also eine List streng innerhalb einer private Methode (nichts, was andere Anwendungen kaputt machen würde, wenn es geändert würde, und ich könnte jede Verwendung in meiner IDE leicht finden/ersetzen), ist es immer noch von Vorteil für die Lesbarkeit, auf eine Abstraktion zu programmieren. Denn Abstraktionen sind einfacher als Implementierungsdetails. Man könnte sagen, dass die Programmierung nach Abstraktionen eine Möglichkeit ist, sich an die KISS Prinzip.

0 Stimmen

Sehr gute Erklärung. Dieses Argument ist wirklich wertvoll.

4voto

Sanjay Rabari Punkte 2045

Kurze Geschichte: Ein Briefträger wird gebeten, nach Hause zu gehen und die darin enthaltenen Briefe (Briefe, Dokumente, Schecks, Geschenkkarten, Bewerbungen, Liebesbrief) mit der darauf geschriebenen Adresse zuzustellen.

Angenommen, es gibt keine Deckung und der Briefträger wird gebeten, von Haus zu Haus zu gehen und alle Sachen in Empfang zu nehmen und an andere Leute zu liefern, dann kann der Briefträger verwirrt werden.

Es ist also besser, ihn mit einer Hülle zu umhüllen (in unserer Geschichte ist es die Schnittstelle), dann wird er seine Arbeit gut machen.

Die Aufgabe des Postboten besteht nun darin, nur die Umschläge entgegenzunehmen und zuzustellen (er würde sich nicht darum kümmern, was sich im Umschlag befindet).

Erstellen Sie einen Typ von interface nicht den tatsächlichen Typ, sondern die Implementierung mit dem tatsächlichen Typ.

Eine Schnittstelle zu erstellen bedeutet, dass Ihre Komponenten Leicht in den Rest des Codes zu integrieren

Ich gebe Ihnen ein Beispiel.

haben Sie die AirPlane-Schnittstelle wie unten dargestellt.

interface Airplane{
    parkPlane();
    servicePlane();
}

Angenommen, Sie haben in Ihrer Controller-Klasse von Planes Methoden wie

parkPlane(Airplane plane)

und

servicePlane(Airplane plane)

in Ihrem Programm implementiert. Es wird nicht UNTERBRECHUNG Ihren Code. Ich meine, er muss sich nicht ändern, solange er Argumente als AirPlane .

Denn es wird jedes Flugzeug trotz des aktuellen Typs akzeptiert, flyer , highflyr , fighter など。

Auch in einer Sammlung:

List<Airplane> plane; // Wird alle Ihre Flugzeuge nehmen.

Das folgende Beispiel soll Ihnen das Verständnis erleichtern.


Sie haben ein Kampfflugzeug, das es einsetzt, also

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

Das Gleiche gilt für HighFlyer und andere Klassen:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

Denken Sie nun Ihre Controller-Klassen mit AirPlane mehrere Male,

Angenommen, Ihre Controller-Klasse ist ControlPlane wie unten,

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

Hier kommt Magie ins Spiel, denn Sie können Ihr neues AirPlane Instanzen des Typs so viele Sie wollen und Sie ändern nicht den Code von ControlPlane Klasse.

Sie können eine Instanz hinzufügen...

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

Sie können auch Instanzen von zuvor erstellten Typen entfernen.

4voto

Richard Punkte 963

Sie eignet sich auch gut für Unit-Tests, da Sie Ihre eigenen Klassen (die die Anforderungen der Schnittstelle erfüllen) in eine Klasse injizieren können, die von ihr abhängt

3voto

Trae Barlow Punkte 31

C++ Erklärung.

Stellen Sie sich eine Schnittstelle als die öffentlichen Methoden Ihrer Klasse vor.

Sie könnten dann eine Vorlage erstellen, die von diesen öffentlichen Methoden "abhängig" ist, um ihre eigene Funktion auszuführen (sie führt Funktionsaufrufe aus, die in der öffentlichen Schnittstelle der Klasse definiert sind). Nehmen wir an, diese Vorlage ist ein Container, wie eine Vektorklasse, und die Schnittstelle, von der sie abhängt, ist ein Suchalgorithmus.

Jede Algorithmusklasse, die die Funktionen/Schnittstelle definiert, die Vector aufruft, erfüllt den "Vertrag" (wie jemand in der ursprünglichen Antwort erklärte). Die Algorithmen müssen nicht einmal derselben Basisklasse angehören; die einzige Voraussetzung ist, dass die Funktionen/Methoden, von denen der Vector abhängt (Schnittstelle), in Ihrem Algorithmus definiert sind.

Der Sinn des Ganzen ist, dass Sie jeden beliebigen Suchalgorithmus/jede beliebige Suchklasse anbieten können, solange er/sie die Schnittstelle bietet, von der Vector abhängt (Blasensuche, sequentielle Suche, Schnellsuche).

Sie könnten auch andere Container (Listen, Warteschlangen) entwerfen, die denselben Suchalgorithmus wie Vector nutzen, indem sie die Schnittstelle/Vertrag erfüllen, von der Ihr Suchalgorithmus abhängt.

Dies spart Zeit (OOP-Grundsatz "Wiederverwendung von Code"), da Sie einen Algorithmus nur einmal schreiben müssen und nicht immer wieder für jedes neue Objekt, das Sie erstellen, ohne die Sache durch einen ausufernden Vererbungsbaum zu verkomplizieren.

Was das "Verpassen" der Funktionsweise angeht, so ist das (zumindest in C++) von entscheidender Bedeutung, da der größte Teil des Frameworks der Standard TEMPLATE Library auf diese Weise funktioniert.

Natürlich ändert sich bei der Verwendung von Vererbung und abstrakten Klassen die Methodik der Programmierung für eine Schnittstelle, aber das Prinzip ist das gleiche: Ihre öffentlichen Funktionen/Methoden sind die Schnittstelle Ihrer Klasse.

Dies ist ein großes Thema und eines der Grundprinzipien von Design Patterns.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X