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.

38voto

Lasse V. Karlsen Punkte 364542

Sie sollten sich mit der Umkehrung der Kontrolle befassen:

In einem solchen Szenario würden Sie dies nicht schreiben:

IInterface classRef = new ObjectWhatever();

Sie würden etwa so schreiben:

IInterface classRef = container.Resolve<IInterface>();

Dies würde in eine regelbasierte Einrichtung in der container Objekt und konstruieren das eigentliche Objekt für Sie, das ObjectWhatever sein könnte. Das Wichtigste ist, dass Sie diese Regel durch etwas ersetzen könnten, das einen anderen Objekttyp verwendet, und Ihr Code würde trotzdem funktionieren.

Wenn wir IoC vom Tisch lassen, können Sie Code schreiben, der weiß, dass er mit einem Objekt sprechen kann die etwas Bestimmtes tut aber nicht, welche Art von Objekt oder wie es dies tut.

Dies wäre bei der Übergabe von Parametern nützlich.

Was Ihre Frage in Klammern angeht: "Wie kann man eine Methode schreiben, die ein Objekt aufnimmt, das eine Schnittstelle implementiert? Ist das möglich?", würden Sie in C# einfach den Schnittstellentyp für den Parametertyp verwenden, etwa so:

public void DoSomethingToAnObject(IInterface whatever) { ... }

Dies fügt sich nahtlos in die "Rede mit einem Objekt, das etwas Bestimmtes tut" ein. Die oben definierte Methode weiß, was sie von dem Objekt zu erwarten hat, nämlich dass es alles in IInterface implementiert, aber es ist ihr egal, um welche Art von Objekt es sich handelt, sie will nur, dass es sich an den Vertrag hält, was eine Schnittstelle ist.

Zum Beispiel sind Sie wahrscheinlich mit Taschenrechnern vertraut und haben in Ihrem Leben schon einige benutzt, aber meistens sind sie alle unterschiedlich. Sie hingegen wissen, wie ein Standardtaschenrechner funktionieren sollte. Sie können sie also alle benutzen, auch wenn Sie die spezifischen Funktionen, die jeder Taschenrechner hat und die keiner der anderen hat, nicht nutzen können.

Das ist das Schöne an Schnittstellen. Man kann ein Stück Code schreiben, das weiß, dass es Objekte übergeben bekommt, von denen es ein bestimmtes Verhalten erwarten kann. Dabei ist es völlig egal, um welche Art von Objekt es sich handelt, Hauptsache, es unterstützt das gewünschte Verhalten.

Ich möchte Ihnen ein konkretes Beispiel nennen.

Wir haben ein maßgeschneidertes Übersetzungssystem für Windows-Formulare entwickelt. Dieses System durchläuft die Steuerelemente eines Formulars und übersetzt den Text in jedem einzelnen. Das System weiß, wie man mit grundlegenden Steuerelementen umgeht, wie z. B. dem Typ des Steuerelements, das eine Texteigenschaft hat, und ähnlichen grundlegenden Dingen, aber für alles, was grundlegend ist, reicht es nicht aus.

Da Steuerelemente von vordefinierten Klassen erben, über die wir keine Kontrolle haben, gibt es drei Möglichkeiten, wie wir vorgehen können:

  1. Unterstützung für unser Übersetzungssystem, um zu erkennen, mit welcher Art von Steuerung es arbeitet, und die richtigen Bits zu übersetzen (Wartungsalptraum)
  2. Unterstützung in Basisklassen einbauen (unmöglich, da alle Steuerelemente von verschiedenen vordefinierten Klassen erben)
  3. Schnittstellenunterstützung hinzufügen

Alle unsere Steuerelemente implementieren ILocalizable, eine Schnittstelle, die uns eine Methode zur Verfügung stellt, nämlich die Möglichkeit, "sich selbst" in einen Container mit Übersetzungstext/Regeln zu übersetzen. Das Formular muss also nicht wissen, welche Art von Steuerelement es gefunden hat, es muss nur wissen, dass es die spezifische Schnittstelle implementiert und dass es eine Methode gibt, die es aufrufen kann, um das Steuerelement zu lokalisieren.

33 Stimmen

Warum sollte man IoC gleich zu Beginn erwähnen, da dies nur noch mehr Verwirrung stiften würde.

1 Stimmen

Zustimmen, würde ich sagen, Programmierung gegen Schnittstellen ist nur eine Technik, um IoC einfacher und zuverlässiger zu machen.

37voto

Bill Rosmus Punkte 2881

Code für die Schnittstelle und nicht für die Implementierung hat NICHTS mit Java zu tun, auch nicht mit dem Interface-Konstrukt.

Dieses Konzept wurde in den Büchern Patterns / Gang of Four bekannt gemacht, war aber wahrscheinlich schon lange vorher vorhanden. Das Konzept existierte sicherlich schon lange bevor es Java gab.

Das Java-Interface-Konstrukt wurde (unter anderem) zur Unterstützung dieser Idee geschaffen, und die Menschen haben sich zu sehr auf das Konstrukt als Mittelpunkt der Bedeutung und nicht auf die ursprüngliche Absicht konzentriert. Es ist jedoch der Grund, warum wir in Java, C++, C# usw. öffentliche und private Methoden und Attribute haben.

Es bedeutet, dass Sie einfach mit der öffentlichen Schnittstelle eines Objekts oder Systems interagieren. Machen Sie sich keine Gedanken darüber, wie es das tut, was es intern tut, und erahnen Sie es auch nicht. Kümmern Sie sich nicht darum, wie es implementiert ist. In objektorientiertem Code gibt es deshalb öffentliche und private Methoden/Attribute. Wir sollen die öffentlichen Methoden verwenden, denn die privaten Methoden sind nur für die interne Verwendung innerhalb der Klasse gedacht. Sie bilden die Implementierung der Klasse und können bei Bedarf geändert werden, ohne die öffentliche Schnittstelle zu verändern. Was die Funktionalität betrifft, so wird eine Methode einer Klasse jedes Mal, wenn Sie sie mit denselben Parametern aufrufen, dieselbe Operation mit demselben erwarteten Ergebnis ausführen. Sie erlaubt es dem Autor, die Funktionsweise der Klasse, ihre Implementierung, zu ändern, ohne die Interaktion mit der Klasse zu beeinträchtigen.

Und Sie können für die Schnittstelle und nicht für die Implementierung programmieren, ohne jemals ein Interface-Konstrukt zu verwenden. In C++, das nicht über ein Schnittstellenkonstrukt verfügt, können Sie auf die Schnittstelle und nicht auf die Implementierung programmieren. Sie können zwei massive Unternehmenssysteme viel robuster integrieren, solange sie über öffentliche Schnittstellen (Verträge) interagieren, anstatt Methoden auf Objekten innerhalb der Systeme aufzurufen. Von den Schnittstellen wird erwartet, dass sie bei gleichen Eingabeparametern immer auf die gleiche Art und Weise reagieren; wenn sie an der Schnittstelle und nicht an der Implementierung implementiert werden. Das Konzept funktioniert an vielen Stellen.

Verwerfen Sie den Gedanken, dass Java-Schnittstellen irgendetwas mit dem Konzept "Program to the Interface, Not the Implementation" zu tun haben. Sie können helfen, das Konzept anzuwenden, aber sie sind pas das Konzept.

2 Stimmen

Der erste Satz sagt schon alles. Dies sollte die akzeptierte Antwort sein.

14voto

Todd Smith Punkte 16604

Es klingt, als wüssten Sie, wie Schnittstellen funktionieren, sind sich aber nicht sicher, wann sie eingesetzt werden sollten und welche Vorteile sie bieten. Hier sind ein paar Beispiele, wann eine Schnittstelle sinnvoll ist:

// if I want to add search capabilities to my application and support multiple search
// engines such as Google, Yahoo, Live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

dann könnte ich GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider, usw. erstellen.

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

dann JpegImageLoader, GifImageLoader, PngImageLoader, usw. erstellen.

Die meisten Add-Ins und Plug-In-Systeme arbeiten mit Schnittstellen.

Eine weitere beliebte Anwendung ist das Repository-Muster. Angenommen, ich möchte eine Liste von Postleitzahlen aus verschiedenen Quellen laden

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

dann könnte ich ein XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository usw. erstellen. Für meine Webanwendungen erstelle ich oft frühzeitig XML-Repositorys, damit ich etwas zum Laufen bringen kann, bevor die SQL-Datenbank fertig ist. Sobald die Datenbank fertig ist, schreibe ich ein SQLRepository, das die XML-Version ersetzt. Der Rest meines Codes bleibt unverändert, da er ausschließlich über Schnittstellen läuft.

Methoden können Schnittstellen akzeptieren wie:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}

13voto

Jatin Punkte 29670

Es gibt eine Menge Erklärungen, aber um es noch einfacher zu machen. Nehmen Sie zum Beispiel eine List . Man kann eine Liste mit as implementieren:

  1. Ein internes Array
  2. Eine verknüpfte Liste
  3. Andere Umsetzungen

Durch den Aufbau einer Schnittstelle, beispielsweise einer List . Sie codieren nur die Definition von Liste oder was List in der Realität bedeutet.

Sie können intern jede Art von Implementierung verwenden, beispielsweise eine array Umsetzung. Aber angenommen, Sie möchten die Implementierung aus irgendeinem Grund ändern, z. B. wegen eines Fehlers oder der Leistung. Dann müssen Sie nur die Deklaration ändern List<String> ls = new ArrayList<String>() à List<String> ls = new LinkedList<String>() .

Nirgendwo sonst im Code müssen Sie etwas anderes ändern; denn alles andere wurde auf der Definition von List .

12voto

Ed S. Punkte 118985

Es macht Ihren Code viel erweiterbar und einfacher zu pflegen, wenn Sie Gruppen von ähnlichen Klassen haben. Ich bin ein junger Programmierer, also kein Experte, aber ich habe gerade ein Projekt abgeschlossen, das etwas Ähnliches erforderte.

Ich arbeite an einer clientseitigen Software, die mit einem Server kommuniziert, auf dem ein medizinisches Gerät läuft. Wir entwickeln eine neue Version dieses Geräts, die einige neue Komponenten enthält, die der Kunde gelegentlich konfigurieren muss. Es gibt zwei Arten von neuen Komponenten, die sich zwar unterscheiden, aber auch sehr ähnlich sind. Im Grunde musste ich zwei Konfigurationsformulare, zwei Listenklassen, zwei von allem erstellen.

Ich beschloss, dass es am besten wäre, eine abstrakte Basisklasse für jeden Kontrolltyp zu erstellen, die fast die gesamte eigentliche Logik enthalten würde, und dann abgeleitete Typen, die sich um die Unterschiede zwischen den beiden Komponenten kümmern. Die Basisklassen wären jedoch nicht in der Lage gewesen, Operationen mit diesen Komponenten auszuführen, wenn ich mich die ganze Zeit um die Typen hätte kümmern müssen (nun ja, sie hätten es gekonnt, aber es hätte eine "if"-Anweisung oder einen Schalter in jeder Methode gegeben).

Ich habe eine einfache Schnittstelle für diese Komponenten definiert, und alle Basisklassen sprechen mit dieser Schnittstelle. Wenn ich jetzt etwas ändere, funktioniert es so gut wie überall, und ich habe keinen doppelten Code.

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