Ich lerne und programmiere seit einiger Zeit in C #. Trotzdem kann ich die Nützlichkeit von Schnittstellen nicht beurteilen. Sie bringen zu wenig auf den Tisch. Abgesehen von der Bereitstellung der Funktionssignaturen tun sie nichts. Wenn ich mich an die Namen und Unterschriften der Funktionen erinnern kann, die implementiert werden müssen, sind sie nicht erforderlich. Sie sind nur dazu da, um sicherzustellen, dass die genannten Funktionen (in der Schnittstelle) in der übernehmenden Klasse implementiert sind.
C # ist eine großartige Sprache, aber manchmal hat man das Gefühl, dass Microsoft das Problem zuerst erstellt (keine Mehrfachvererbung zulässt) und dann die Lösung bereitstellt, die ziemlich langwierig ist.
Das ist mein Verständnis, das auf begrenzten Codierungserfahrungen basiert. Was halten Sie von Schnittstellen? Wie oft benutzt du sie und wie oft machst du das?
quelle
Antworten:
Richtig. Das ist ein großartiger Vorteil, der die Funktion rechtfertigt. Wie bereits erwähnt, ist eine Schnittstelle eine vertragliche Verpflichtung zur Implementierung bestimmter Methoden, Eigenschaften und Ereignisse. Der überzeugende Vorteil einer statisch typisierten Sprache besteht darin, dass der Compiler überprüfen kann, ob ein Vertrag, auf den sich Ihr Code stützt, tatsächlich erfüllt ist.
Trotzdem sind Schnittstellen eine ziemlich schwache Möglichkeit, vertragliche Verpflichtungen darzustellen. Wenn Sie eine stärkere und flexiblere Möglichkeit zur Darstellung vertraglicher Verpflichtungen wünschen, schauen Sie sich die Codevertragsfunktion an, die mit der letzten Version von Visual Studio ausgeliefert wurde.
Nun, ich bin froh, dass es dir gefällt.
Alle komplexen Softwaredesigns sind das Ergebnis der Abwägung widersprüchlicher Funktionen und des Versuchs, den "Sweet Spot" zu finden, der große Vorteile bei geringen Kosten bietet. Wir haben durch schmerzhafte Erfahrungen gelernt, dass Sprachen, die eine Mehrfachvererbung zum Zwecke der gemeinsamen Nutzung von Implementierungen ermöglichen, relativ geringe Vorteile und relativ hohe Kosten haben. Das Zulassen der Mehrfachvererbung nur für Schnittstellen, die keine Implementierungsdetails gemeinsam nutzen, bietet viele Vorteile der Mehrfachvererbung, ohne die meisten Kosten zu verursachen.
quelle
Assume
für Fälle wie Array- oder Aufzählungsmitglieder so viele Aufrufe hinzufügen . Nachdem ich alles hinzugefügt und das statisch verifizierte Durcheinander gesehen hatte, kehrte ich in die Quellcodeverwaltung zurück, da dies die Qualität meines Projekts beeinträchtigte.In diesem Beispiel weiß der PowerSocket also nichts anderes über die anderen Objekte. Die Objekte hängen alle von der Stromversorgung durch den PowerSocket ab, sodass sie IPowerPlug implementieren und sich damit verbinden können.
Schnittstellen sind nützlich, da sie Verträge bereitstellen, mit denen Objekte zusammenarbeiten können, ohne dass sie etwas anderes voneinander wissen müssen.
quelle
Die Schnittstelle soll Ihnen nicht dabei helfen, sich an die zu implementierende Methode zu erinnern, sondern dient dazu , einen Vertrag zu definieren . In foreach P.Brian.Mackey-Beispiel (was sich als falsch herausstellt , aber uns egal ist) definiert IEnumerable einen Vertrag zwischen foreach und einer beliebigen zählbaren Sache. Es heißt: "Wer auch immer Sie sind, solange Sie sich an den Vertrag halten (IEnumerable implementieren), ich verspreche Ihnen, ich werde über alle Ihre Elemente iterieren". Und das ist großartig (für eine nicht dynamische Sprache).
Dank der Schnittstellen können Sie eine sehr geringe Kopplung zwischen zwei Klassen erreichen.
quelle
Schnittstellen sind der beste Weg, um gut entkoppelte Konstrukte zu erhalten.
Beim Schreiben von Tests werden Sie feststellen, dass konkrete Klassen in Ihrer Testumgebung nicht funktionieren.
Beispiel: Sie möchten eine Klasse testen, die von einer Data Access Service- Klasse abhängt . Wenn diese Klasse mit einem Webdienst oder einer Datenbank kommuniziert, wird Ihr Komponententest in Ihrer Testumgebung nicht ausgeführt (und es wurde ein Integrationstest durchgeführt).
Lösung? Verwenden Sie eine Schnittstelle für Ihren Datenzugriffsdienst und verspotten Sie diese Schnittstelle, damit Sie Ihre Klasse als Einheit testen können.
Andererseits spielen WPF und Silverlight beim Binden überhaupt nicht mit Interfaces. Das ist eine ziemlich üble Falte.
quelle
Schnittstellen sind das Rückgrat des (statischen) Polymorphismus! Auf die Schnittstelle kommt es an. Die Vererbung würde ohne Schnittstellen nicht funktionieren, da Unterklassen im Grunde die bereits implementierte Schnittstelle des übergeordneten Elements erben.
Sehr oft. Alles, was steckbar sein muss, ist eine Schnittstelle in meinen Anwendungen. Häufig gibt es ansonsten nicht verwandte Klassen, die dasselbe Verhalten aufweisen müssen. Sie können solche Probleme nicht mit Vererbung lösen.
Benötigen Sie verschiedene Algorithmen, um Operationen mit denselben Daten auszuführen? Verwenden Sie eine Schnittstelle ( siehe Strategiemuster )!
Möchten Sie verschiedene Listenimplementierungen verwenden? Code gegen eine Schnittstelle und der Aufrufer muss sich nicht um die Implementierung kümmern!
Es wird seit langem als bewährte Methode (nicht nur in OOP) angesehen, Schnittstellen zu codieren, und zwar aus einem einzigen Grund: Es ist einfach, eine Implementierung zu ändern, wenn Sie feststellen, dass sie nicht Ihren Anforderungen entspricht. Es ist ziemlich umständlich, wenn Sie versuchen, dies nur durch Mehrfachvererbung zu erreichen, oder wenn Sie leere Klassen erstellen, um die erforderliche Schnittstelle bereitzustellen.
quelle
Sie haben es wahrscheinlich
foreach
als ziemlich nützliches Iterationswerkzeug verwendet und gefunden. Wussten Sie, dass es eine Schnittstelle benötigt, um zu funktionieren, IEnumerable ?Das ist sicherlich ein konkreter Fall, der auf die Nützlichkeit einer Schnittstelle verweist.
quelle
Schnittstellen dienen zur Codierung von Objekten wie ein Stecker zur Haushaltsverkabelung. Würden Sie Ihr Radio direkt an Ihre Hausverkabelung anlöten? Wie wäre es mit Ihrem Staubsauger? Natürlich nicht. Der Stecker und die Steckdose, in die er passt, bilden die "Schnittstelle" zwischen Ihrer Hausverkabelung und dem Gerät, das die Stromversorgung benötigt. Ihre Hausverkabelung muss nichts anderes über das Gerät wissen, als einen dreipoligen geerdeten Stecker zu verwenden und benötigt eine elektrische Spannung von 120VAC <= 15A. Umgekehrt erfordert das Gerät keine geheimen Kenntnisse über die Verkabelung Ihres Hauses. Es verfügt lediglich über eine oder mehrere dreipolige Steckdosen, die 120 VAC <= 15 A liefern.
Schnittstellen haben im Code eine sehr ähnliche Funktion. Ein Objekt kann deklarieren, dass eine bestimmte Variable, ein Parameter oder ein Rückgabetyp von einem Schnittstellentyp ist. Die Schnittstelle kann nicht direkt mit einem
new
Schlüsselwort instanziiert werden , aber mein Objekt kann die Implementierung dieser Schnittstelle erhalten oder finden, mit der es arbeiten muss. Sobald das Objekt seine Abhängigkeit hat, muss es nicht genau wissen, was diese Abhängigkeit ist, sondern es muss nur wissen, dass es Methoden X, Y und Z für die Abhängigkeit aufrufen kann. Implementierungen der Schnittstelle müssen nicht wissen, wie sie verwendet werden, sondern müssen nur wissen, dass von ihnen erwartet wird, dass sie die Methoden X, Y und Z mit bestimmten Signaturen versehen.Indem Sie also mehrere Objekte hinter derselben Schnittstelle abstrahieren, stellen Sie jedem Benutzer von Objekten dieser Schnittstelle eine Reihe von Funktionen zur Verfügung. Sie müssen nicht wissen, dass das Objekt beispielsweise eine Liste, ein Dictionary, eine LinkedList, eine OrderedList oder was auch immer ist. Da Sie wissen, dass dies alles IEnumerables sind, können Sie die Methoden von IEnumerable verwenden, um jedes Element in diesen Auflistungen einzeln durchzugehen. Sie müssen nicht wissen, dass eine Ausgabeklasse ein ConsoleWriter, ein FileWriter, ein NetworkStreamWriter oder sogar ein MulticastWriter ist, der andere Arten von Writern akzeptiert. Alles, was Sie wissen müssen, ist, dass sie alle IWriter (oder was auch immer) sind, und daher haben sie eine "Write" -Methode, in die Sie einen String übergeben können, und dieser String wird ausgegeben.
quelle
Während es für den Programmierer (zumindest anfangs) ein Vergnügen ist, Mehrfachvererbung zu haben, ist dies eine fast triviale Auslassung, und Sie sollten sich (in den meisten Fällen) nicht auf Mehrfachvererbung verlassen. Die Gründe dafür sind komplex, aber wenn Sie wirklich etwas darüber lernen möchten, sollten Sie die Erfahrung aus den beiden bekanntesten (nach TIOBE-Index ) Programmiersprachen in Betracht ziehen , die es unterstützen: C ++ und Python (3. und 8. respektabel).
In Python wird die Mehrfachvererbung unterstützt, wird jedoch von Programmierern fast überall missverstanden. Wenn Sie feststellen, dass Sie wissen, wie es funktioniert, müssen Sie dieses Dokument zum Thema " Reihenfolge der Methodenauflösung" lesen und verstehen . Etwas anderes, was in Python passierte, ist, dass Schnittstellen irgendwie in die Sprache gelangen - Zope.Interfaces.
Für C ++ googeln Sie "Diamanthierarchie C ++" und sehen Sie sich die Hässlichkeit an, die Sie bedecken wird. C ++ - Profis können Mehrfachvererbung verwenden. Alle anderen spielen normalerweise nur herum, ohne zu wissen, was die Ergebnisse sein werden. Eine andere Sache, die zeigt, wie nützlich Schnittstellen sind, ist die Tatsache, dass eine Klasse in vielen Fällen das Verhalten ihrer Eltern vollständig außer Kraft setzen muss. In solchen Fällen ist die übergeordnete Implementierung nicht erforderlich und belastet die untergeordnete Klasse nur mit dem Speicher für die privaten Variablen der übergeordneten Klasse, was im C # -Alter möglicherweise keine Rolle spielt, aber wichtig ist, wenn Sie eingebettete Programmierung ausführen. Wenn Sie eine Schnittstelle verwenden, ist dieses Problem nicht vorhanden.
Zusammenfassend ist festzuhalten, dass Schnittstellen meiner Meinung nach ein wesentlicher Bestandteil von OOP sind, da sie einen Vertrag durchsetzen. Mehrfachvererbung ist in begrenzten Fällen nützlich und normalerweise nur für Leute, die wissen, wie man es benutzt. Wenn Sie also ein Anfänger sind, sind Sie derjenige, der durch das Fehlen einer Mehrfachvererbung behandelt wird - dies gibt Ihnen eine bessere Chance, keinen Fehler zu machen .
Historisch gesehen wurzelt die Idee für eine Benutzeroberfläche weit früher als die C # -Designspezifikationen von Microsoft. Die meisten Leute halten C # für ein Upgrade über Java (in den meisten Hinsichten) und raten, woher C # seine Schnittstellen hat - Java. Protocol ist ein älteres Wort für dasselbe Konzept und viel älter als .NET.
Update: Jetzt sehe ich, dass ich möglicherweise eine andere Frage beantwortet habe - warum Schnittstellen statt Mehrfachvererbung, aber dies schien die Antwort zu sein, die Sie gesucht haben. Außerdem sollte eine OO-Sprache mindestens eine der beiden haben, und die anderen Antworten haben Ihre ursprüngliche Frage abgedeckt.
quelle
Ich kann mir keinen sauberen, objektorientierten C # -Code ohne die Verwendung von Schnittstellen vorstellen. Sie verwenden sie immer dann, wenn Sie die Verfügbarkeit bestimmter Funktionen erzwingen möchten, ohne Klassen zu zwingen, von einer bestimmten Basisklasse zu erben, und dies ermöglicht Ihrem Code, die relevante Ebene der (geringen) Kopplung zu haben.
Ich bin nicht der Meinung, dass Mehrfachvererbung besser ist als Schnittstellen, noch bevor wir darüber streiten, dass Mehrfachvererbung mit eigenen Schmerzen verbunden ist. Schnittstellen sind ein grundlegendes Werkzeug, um Polymorphismus und Wiederverwendung von Code zu ermöglichen. Was braucht man mehr?
quelle
Ich persönlich liebe die abstrakte Klasse und benutze sie mehr als eine Schnittstelle. Der Hauptunterschied besteht in der Integration mit .NET-Schnittstellen wie IDisposable, IEnumerable usw. und mit COM-Interop. Darüber hinaus ist das Schreiben der Schnittstelle etwas einfacher als bei einer abstrakten Klasse, und eine Klasse kann mehr als eine Schnittstelle implementieren, während sie nur von einer Klasse erben kann.
Trotzdem finde ich, dass die meisten Dinge, für die ich eine Schnittstelle verwenden würde, von einer abstrakten Klasse besser bedient werden. Reine virtuelle Funktionen - abstrakte Funktionen - ermöglichen es Ihnen, einen Implementierer zu zwingen, eine Funktion zu definieren, ähnlich wie eine Schnittstelle einen Implementierer zwingt, alle ihre Mitglieder zu definieren.
Normalerweise verwenden Sie jedoch eine Schnittstelle, wenn Sie der Superklasse keinen bestimmten Entwurf auferlegen möchten, während Sie eine abstrakte Klasse verwenden würden, um einen wiederverwendbaren Entwurf zu erhalten, der bereits größtenteils implementiert ist.
Ich habe mit dem System.ComponentModel-Namespace umfangreiche Schnittstellen zum Schreiben von Plug-in-Umgebungen verwendet. Sie kommen ganz praktisch.
quelle
Ich kann sagen, ich beziehe mich darauf. Als ich anfing, etwas über OO und C # zu lernen, bekam ich auch keine Interfaces. Das ist okay. Wir müssen nur auf etwas stoßen, mit dem Sie den Komfort von Schnittstellen schätzen lernen.
Lassen Sie mich zwei Ansätze ausprobieren. Und verzeihen Sie mir die Verallgemeinerungen.
Versuchen Sie 1
Angenommen, Sie sprechen Englisch als Muttersprache. Sie reisen in ein anderes Land, in dem Englisch nicht die Muttersprache ist. Sie benötigen Hilfe. Sie brauchen jemanden, der Ihnen helfen kann.
Fragen Sie: "Hey, sind Sie in den Vereinigten Staaten geboren?" Das ist Vererbung.
Oder fragst du: "Hey, sprichst du Englisch?" Das ist Schnittstelle.
Wenn es Ihnen wichtig ist, was es tut, können Sie sich auf Schnittstellen verlassen. Wenn Sie sich für das interessieren, was ist, verlassen Sie sich auf Vererbung.
Es ist in Ordnung, sich auf die Vererbung zu verlassen. Wenn Sie jemanden brauchen, der Englisch spricht, Tee mag und Fußball mag, sollten Sie besser nach einem Briten fragen. :)
Versuchen Sie 2
Ok, lass uns ein anderes Beispiel versuchen.
Sie verwenden verschiedene Datenbanken und müssen abstrakte Klassen implementieren, um mit ihnen arbeiten zu können. Sie übergeben Ihre Klasse an eine Klasse des DB-Anbieters.
Mehrfachvererbung, sagen Sie? Versuchen Sie das mit dem obigen Fall. Das kannst du nicht. Der Compiler weiß nicht, welche Connect-Methode Sie aufrufen möchten.
Jetzt können wir - zumindest in C # - mit etwas arbeiten, mit dem wir Schnittstellen explizit implementieren können.
Fazit
Die Beispiele sind nicht die besten, aber ich denke, es kommt darauf an.
Sie werden nur Schnittstellen "bekommen", wenn Sie das Bedürfnis danach haben. Bis sie denken Sie, dass sie nicht für Sie sind.
quelle
Es gibt zwei Hauptgründe:
quelle
Durch die Verwendung von Schnittstellen bleibt ein System entkoppelt und kann leichter umgestaltet, geändert und neu bereitgestellt werden. Es ist ein sehr zentrales Konzept für die objektorientierte Orthodoxie, und ich habe es zum ersten Mal erfahren, als C ++ - Gurus "reine abstrakte Klassen" erstellten, die Schnittstellen ziemlich gleichwertig sind.
quelle
Schnittstellen an sich sind nicht sehr nützlich. Bei der Implementierung durch konkrete Klassen sehen Sie jedoch, dass Sie die Flexibilität haben, eine oder mehrere Implementierungen vorzunehmen. Der Vorteil ist, dass das Objekt, das die Schnittstelle verwendet, nicht wissen muss, wie die Details der tatsächlichen Implementierung verlaufen - das nennt man Kapselung.
quelle
Sie werden hauptsächlich für die Wiederverwendbarkeit von Code verwendet. Wenn Sie mit der Schnittstelle codieren, können Sie eine andere Klasse verwenden, die von dieser Schnittstelle erbt und nicht alles aufteilt.
Außerdem sind sie in Webservices sehr nützlich, bei denen Sie dem Client mitteilen möchten, was eine Klasse tut (damit sie sie verwenden kann), ihnen aber nicht den tatsächlichen Code geben möchten.
quelle
Als junger Programmierer / Entwickler erkennen Sie beim Erlernen von C # möglicherweise nicht den Nutzen der Benutzeroberfläche, da Sie Ihre Codes möglicherweise mithilfe Ihrer Klassen schreiben und der Code problemlos funktioniert. Im realen Szenario erfordert das Erstellen einer skalierbaren, robusten und wartbaren Anwendung jedoch die Verwendung von Einige Architekturen und Muster, die nur über die Schnittstelle möglich sind, befinden sich beispielsweise in Abhängigkeitsinjektion.
quelle
Eine echte Implementierung:
Sie können ein Objekt als Schnittstellentyp umwandeln:
Sie können eine Liste einer Schnittstelle erstellen
Mit diesen Objekten können Sie auf eine der Schnittstellenmethoden oder -eigenschaften zugreifen. Auf diese Weise können Sie eine Schnittstelle für Ihren Teil eines Programms definieren. Und bauen Sie die Logik darauf auf. Dann kann eine andere Person Ihre Schnittstelle in ihre Geschäftsobjekte implementieren. Wenn sich die BOs ändern, können sie die Logik für die Schnittstellenkomponenten ändern und erfordern keine Änderung der Logik für Ihr Teil.
quelle
Schnittstellen ermöglichen Modularität im Plug-in-Stil, indem sie Klassen einen Mechanismus bieten, mit dem sie bestimmte Arten von Nachrichten, die Ihr System übermittelt, verstehen (und abonnieren) können. Ich werde näher darauf eingehen.
In Ihrer Anwendung legen Sie fest, dass jedes Mal, wenn ein Formular geladen oder neu geladen wird, alle darin enthaltenen Elemente gelöscht werden sollen. Sie definieren eine
IClear
Schnittstelle, die implementiert wirdClear
. Darüber hinaus entscheiden Sie, dass das Formular versuchen soll, seinen Status beizubehalten, wenn der Benutzer auf die Schaltfläche Speichern klickt. SomitISave
erhält alles, was daran festhält, eine Nachricht, um seinen Zustand beizubehalten . In der Praxis verarbeiten die meisten Schnittstellen natürlich mehrere Nachrichten.Das Besondere an Schnittstellen ist, dass ein gemeinsames Verhalten ohne Vererbung erreicht werden kann. Die Klasse, die eine bestimmte Schnittstelle implementiert, versteht einfach, wie sie sich verhält, wenn ein Befehl ausgegeben wird (eine Befehlsnachricht) oder wie sie auf Abfragen reagiert (eine Abfragenachricht). Im Wesentlichen verstehen die Klassen in Ihrer Anwendung die Nachrichten, die Ihre Anwendung bereitstellt. Dies erleichtert den Aufbau eines modularen Systems, in das Dinge eingesteckt werden können.
In den meisten Sprachen gibt es Mechanismen (wie LINQ ) zum Abfragen von Dingen, die sich an eine Schnittstelle halten. Dies hilft Ihnen normalerweise dabei, die bedingte Logik zu eliminieren, da Sie nicht verschiedenen Dingen (die nicht aus derselben Vererbungskette stammen müssen) mitteilen müssen, wie sie sich ähnlich verhalten sollen (gemäß einer bestimmten Nachricht). Stattdessen sammeln Sie alles, was eine bestimmte Nachricht versteht (und halten sich an eine Schnittstelle), und veröffentlichen die Nachricht.
Zum Beispiel könnten Sie ersetzen ...
...mit:
Was sich im Grunde nach Folgendem anhört:
Auf diese Weise können wir programmgesteuert vermeiden, dass jedes einzelne Element aufgefordert wird, sich selbst zu löschen. Und wenn in Zukunft löschbare Elemente hinzugefügt werden, reagieren sie einfach ohne zusätzlichen Code.
quelle
Das Folgende ist Pseudocode:
Die letzten Klassen können völlig unterschiedliche Implementierungen sein.
Sofern keine Mehrfachvererbung möglich ist, wird durch die Vererbung die Implementierung der übergeordneten Klasse erzwungen, wodurch die Dinge starrer werden. Die Programmierung anhand von Schnittstellen hingegen kann dazu führen, dass Ihr Code oder ein Framework äußerst flexibel ist. Wenn Sie jemals auf einen Fall stoßen, in dem Sie sich gewünscht haben, Klassen in einer Vererbungskette zu tauschen, werden Sie verstehen, warum.
Beispielsweise könnte ein Framework, das einen Reader bereitstellt, der ursprünglich zum Lesen von Daten von der Festplatte vorgesehen war, erneut implementiert werden, um etwas in der gleichen Art, jedoch auf eine völlig andere Art und Weise zu tun. Wie zum Beispiel Morse-Code interpretieren.
quelle