Der Grund für Schnittstellen entgeht mir wirklich. Soweit ich weiß, handelt es sich um eine Art Problemumgehung für die nicht vorhandene Mehrfachvererbung, die in C # nicht vorhanden ist (so wurde mir gesagt).
Ich sehe nur, dass Sie einige Mitglieder und Funktionen vordefinieren, die dann in der Klasse erneut neu definiert werden müssen. Dadurch wird die Schnittstelle überflüssig. Es fühlt sich einfach syntaktisch an… na ja, Junk für mich (Bitte keine Beleidigung gemeint. Junk wie in nutzlosen Sachen).
In dem unten angegebenen Beispiel, das einem anderen C # -Schnittstellen-Thread beim Stapelüberlauf entnommen wurde, würde ich anstelle einer Schnittstelle nur eine Basisklasse namens Pizza erstellen.
einfaches Beispiel (entnommen aus einem anderen Stapelüberlaufbeitrag)
public interface IPizza
{
public void Order();
}
public class PepperoniPizza : IPizza
{
public void Order()
{
//Order Pepperoni pizza
}
}
public class HawaiiPizza : IPizza
{
public void Order()
{
//Order HawaiiPizza
}
}
struct
Typen herzustellen .Antworten:
Der Punkt ist, dass die Schnittstelle einen Vertrag darstellt . Eine Reihe öffentlicher Methoden, die jede implementierende Klasse haben muss. Technisch gesehen regelt die Schnittstelle nur die Syntax, dh welche Methoden es gibt, welche Argumente sie erhalten und was sie zurückgeben. Normalerweise kapseln sie auch die Semantik, allerdings nur durch Dokumentation.
Sie können dann verschiedene Implementierungen einer Schnittstelle haben und diese nach Belieben austauschen. In Ihrem Beispiel können Sie, da jede Pizzainstanz eine
IPizza
ist,IPizza
überall dort verwenden, wo Sie eine Instanz eines unbekannten Pizzatyps verarbeiten. Jede Instanz, von der der Typ erbt,IPizza
ist garantiert bestellbar, da sie über eineOrder()
Methode verfügt.Python ist nicht statisch typisiert, daher werden Typen zur Laufzeit beibehalten und nachgeschlagen. Sie können also versuchen, eine
Order()
Methode für ein beliebiges Objekt aufzurufen . Die Laufzeit ist glücklich, solange das Objekt eine solche Methode hat und wahrscheinlich nur mit den Schultern zuckt und »Meh.« Sagt, wenn dies nicht der Fall ist. Nicht so in C #. Der Compiler ist für die korrekten Aufrufe verantwortlich. Wenn er nur einen zufälligenobject
Aufruf enthält, weiß der Compiler noch nicht, ob die Instanz zur Laufzeit über diese Methode verfügt. Aus Sicht des Compilers ist es ungültig, da es es nicht überprüfen kann. (Sie können solche Dinge mit Reflexion oder der tundynamic
Schlüsselwort , aber das geht momentan ein bisschen weit, denke ich.)Beachten Sie auch, dass eine Schnittstelle im üblichen Sinne nicht unbedingt ein C # sein muss
interface
, sondern auch eine abstrakte Klasse oder sogar eine normale Klasse sein kann (was nützlich sein kann, wenn alle Unterklassen einen gemeinsamen Code verwenden müssen - in den meisten Fälleninterface
reicht jedoch aus).quelle
foo
Implementierungen kenntIWidget
,foo.woozle()
in der Dokumentation nachsehenIWidget
und wissen, was diese Methode tun soll . Der Programmierer hat möglicherweise keine Möglichkeit zu wissen, woher der Code für die tatsächliche Implementierung stammt, aber jeder Typ, der denIWidget
Schnittstellenvertrag einhält, wirdfoo
in Übereinstimmung mit diesem Vertrag implementiert . In einer dynamischen Sprache hingegen gäbe es keinen klaren Bezugspunkt für das, wasfoo.woozle()
bedeuten sollte.Niemand hat wirklich klar erklärt, wie nützlich Schnittstellen sind, also werde ich es versuchen (und ein wenig eine Idee aus Shamims Antwort stehlen).
Nehmen wir die Idee eines Pizza-Bestellservices. Sie können mehrere Arten von Pizzen haben und eine gemeinsame Aktion für jede Pizza ist die Vorbereitung der Bestellung im System. Jede Pizza muss zubereitet werden, aber jede Pizza wird anders zubereitet . Wenn zum Beispiel eine gefüllte Krustenpizza bestellt wird, muss das System wahrscheinlich überprüfen, ob bestimmte Zutaten im Restaurant verfügbar sind, und diejenigen beiseite legen, die für Pizza mit tiefem Gericht nicht benötigt werden.
Wenn Sie dies in Code schreiben, können Sie dies technisch einfach tun
Für Deep Dish-Pizzen (in C # -Begriffen) müssen jedoch möglicherweise andere Eigenschaften eingestellt werden
Prepare()
Methode als bei gefüllter Kruste festgelegt werden. Daher stehen Ihnen viele optionale Eigenschaften zur Verfügung, und die Klasse lässt sich nicht gut skalieren (was passiert, wenn Sie neue hinzufügen) Pizzasorten).Der richtige Weg, dies zu lösen, ist die Verwendung der Schnittstelle. Die Schnittstelle erklärt, dass alle Pizzas zubereitet werden können, aber jede Pizza kann anders zubereitet werden. Wenn Sie also die folgenden Schnittstellen haben:
Jetzt muss Ihr Bestellcode nicht mehr genau wissen, welche Pizzasorten bestellt wurden, um die Zutaten zu verarbeiten. Es hat nur:
Obwohl jede Art von Pizza anders zubereitet wird, muss es diesem Teil des Codes egal sein, um welche Art von Pizza es sich handelt. Er weiß nur, dass es sich um Pizza handelt, und daher wird bei jedem Aufruf
Prepare
automatisch jede Pizza korrekt zubereitet basierend auf seinem Typ, auch wenn die Sammlung mehrere Arten von Pizzen enthält.quelle
public
. Also innerhalb der Schnittstelle sollte es nur seinvoid Prepare();
Für mich wurde der Punkt zu Beginn erst klar, als Sie aufhörten, sie als Dinge zu betrachten, die das Schreiben Ihres Codes einfacher / schneller machen - dies ist nicht ihr Zweck. Sie haben eine Reihe von Verwendungszwecken:
(Dies wird die Pizza-Analogie verlieren, da es nicht sehr einfach ist, eine Verwendung davon zu visualisieren.)
Angenommen, Sie machen ein einfaches Spiel auf dem Bildschirm und es werden Kreaturen angezeigt, mit denen Sie interagieren.
A: Sie können die Wartung Ihres Codes in Zukunft vereinfachen, indem sie eine lose Kopplung zwischen Ihrem Front-End und Ihrer Back-End-Implementierung einführen.
Sie könnten dies zunächst schreiben, da es nur Trolle geben wird:
Vorderes Ende:
Zwei Wochen später entscheidet das Marketing, dass Sie auch Orks brauchen, da diese auf Twitter darüber lesen. Sie müssten also Folgendes tun:
Vorderes Ende:
Und Sie können sehen, wie dies anfängt, chaotisch zu werden. Sie können hier eine Schnittstelle verwenden, damit Ihr Front-End einmal geschrieben und (hier das wichtige Bit) getestet wird. Anschließend können Sie nach Bedarf weitere Back-End-Elemente einstecken:
Frontend ist dann:
Das Frontend kümmert sich jetzt nur noch um die Schnittstelle ICreature - es geht nicht um die interne Implementierung eines Trolls oder eines Orks, sondern nur um die Tatsache, dass sie ICreature implementieren.
Ein wichtiger Punkt, den Sie unter diesem Gesichtspunkt beachten sollten, ist, dass Sie auch leicht eine abstrakte Kreaturenklasse hätten verwenden können, und aus dieser Perspektive hat dies den gleichen Effekt.
Und Sie könnten die Kreation in eine Fabrik extrahieren:
Und unser Frontend würde dann werden:
Das Frontend muss jetzt nicht einmal mehr auf die Bibliothek verweisen, in der Troll und Orc implementiert sind (vorausgesetzt, die Fabrik befindet sich in einer separaten Bibliothek) - es muss überhaupt nichts über sie wissen.
B: Angenommen, Sie haben Funktionen, die nur einige Kreaturen in Ihrer ansonsten homogenen Datenstruktur haben , z
Frontend könnte dann sein:
C: Verwendung für die Abhängigkeitsinjektion
Die meisten Abhängigkeitsinjektions-Frameworks sind einfacher zu bearbeiten, wenn eine sehr lockere Kopplung zwischen dem Front-End-Code und der Back-End-Implementierung besteht. Wenn wir unser Fabrikbeispiel oben nehmen und unsere Fabrik eine Schnittstelle implementieren lassen:
Unser Front-End könnte dies dann (z. B. einen MVC-API-Controller) über den Konstruktor injizieren lassen (normalerweise):
Mit unserem DI-Framework (z. B. Ninject oder Autofac) können wir sie so einrichten, dass zur Laufzeit eine Instanz von CreatureFactory erstellt wird, wenn eine ICreatureFactory in einem Konstruktor benötigt wird - dies macht unseren Code schön und einfach.
Dies bedeutet auch, dass wir beim Schreiben eines Komponententests für unseren Controller eine verspottete ICreatureFactory bereitstellen können (z. B. wenn die konkrete Implementierung einen DB-Zugriff erfordert, möchten wir nicht, dass unsere Komponententests davon abhängen) und den Code in unserem Controller einfach testen können .
D: Es gibt andere Verwendungszwecke, z. B. haben Sie zwei Projekte A und B, die aus "alten" Gründen nicht gut strukturiert sind, und A hat einen Verweis auf B.
In B finden Sie dann Funktionen, die eine Methode bereits in A aufrufen müssen. Sie können dies nicht mit konkreten Implementierungen tun, da Sie einen Zirkelverweis erhalten.
Sie können eine Schnittstelle in B deklarieren lassen, die die Klasse in A dann implementiert. Ihrer Methode in B kann eine Instanz einer Klasse übergeben werden, die die Schnittstelle problemlos implementiert, obwohl das konkrete Objekt vom Typ in A ist.
quelle
Hier sind Ihre Beispiele noch einmal erklärt:
quelle
Die obigen Beispiele machen wenig Sinn. Sie können alle oben genannten Beispiele mit Klassen ausführen (abstrakte Klasse, wenn Sie möchten, dass sie sich nur als Vertrag verhält ):
Sie erhalten das gleiche Verhalten wie bei der Schnittstelle. Sie können ein erstellen
List<Food>
und wiederholen, ohne zu wissen, welche Klasse oben steht.Ein adäquateres Beispiel wäre die Mehrfachvererbung:
Dann können Sie alle als verwenden
MenuItem
und sich nicht darum kümmern, wie sie mit jedem Methodenaufruf umgehen.quelle
Einfache Erklärung mit Analogie
Das zu lösende Problem: Was ist der Zweck des Polymorphismus?
Analogie: Ich bin also ein Vorarbeiter auf einer Baustelle.
Handwerker gehen die ganze Zeit auf der Baustelle. Ich weiß nicht, wer durch diese Türen gehen wird. Aber ich sage ihnen im Grunde, was zu tun ist.
Das Problem mit dem obigen Ansatz ist, dass ich: (i) wissen muss, wer in diese Tür kommt, und je nachdem, wer es ist, muss ich ihnen sagen, was zu tun ist. Das heißt, ich muss alles über einen bestimmten Handel wissen. Mit diesem Ansatz sind Kosten / Nutzen verbunden:
Die Auswirkungen des Wissens, was zu tun ist:
Das heißt, wenn sich der Code des Zimmermanns von:
BuildScaffolding()
nach ändertBuildScaffold()
(dh eine geringfügige Namensänderung), muss ich auch die aufrufende Klasse (dh dieForeperson
Klasse) ändern - Sie müssen statt (im Grunde genommen) zwei Änderungen am Code vornehmen ) nur einer. Mit Polymorphismus müssen Sie (im Grunde) nur eine Änderung vornehmen, um das gleiche Ergebnis zu erzielen.Zweitens müssen Sie nicht ständig fragen: Wer sind Sie? ok mach das ... wer bist du? ok mach das ..... Polymorphismus - es trocknet diesen Code und ist in bestimmten Situationen sehr effektiv:
Mit Polymorphismus können Sie problemlos zusätzliche Klassen von Handwerkern hinzufügen, ohne vorhandenen Code zu ändern. (dh das zweite der SOLID-Konstruktionsprinzipien: Open-Close-Prinzip).
Die Lösung
Stellen Sie sich ein Szenario vor, in dem ich, egal wer die Tür betritt, sagen kann: "Work ()" und sie ihre Respektaufgaben erledigen, auf die sie sich spezialisiert haben: Der Klempner würde sich um Rohre kümmern, und der Elektriker würde sich um Drähte kümmern.
Der Vorteil dieses Ansatzes ist, dass: (i) ich nicht genau wissen muss, wer durch diese Tür hereinkommt - alles, was ich wissen muss, ist, dass sie eine Art Tradie sind und arbeiten können, und zweitens , (ii) Ich muss nichts über diesen bestimmten Handel wissen. Der Tradie wird sich darum kümmern.
Also stattdessen:
Ich kann so etwas machen:
Was ist der Vorteil?
Der Vorteil ist, dass der Vorarbeiter seinen Code nicht ändern muss, wenn sich die spezifischen Arbeitsanforderungen des Tischlers usw. ändern - er muss es nicht wissen oder sich darum kümmern. Alles was zählt ist, dass der Schreiner weiß, was unter Arbeit zu verstehen ist (). Zweitens, wenn ein neuer Typ von Bauarbeiter auf die Baustelle kommt, muss der Vorarbeiter nichts über das Handwerk wissen - der Vorarbeiter kümmert sich nur darum, ob der Bauarbeiter (.eg Schweißer, Glaser, Fliesenleger usw.) dies kann erledige etwas Arbeit ().
Illustriertes Problem und Lösung (mit und ohne Schnittstellen):
Keine Schnittstelle (Beispiel 1):
Keine Schnittstelle (Beispiel 2):
Mit einer Schnittstelle:
Zusammenfassung
Über eine Benutzeroberfläche können Sie die Person dazu bringen, die ihnen zugewiesene Arbeit zu erledigen, ohne dass Sie genau wissen, wer sie sind oder was sie genau tun kann. Auf diese Weise können Sie problemlos neue Arten (des Handels) hinzufügen, ohne Ihren vorhandenen Code zu ändern (technisch gesehen ändern Sie ihn ein kleines bisschen), und das ist der eigentliche Vorteil eines OOP-Ansatzes gegenüber einer funktionaleren Programmiermethode.
Wenn Sie eines der oben genannten Dinge nicht verstehen oder wenn es nicht klar ist, fragen Sie in einem Kommentar und ich werde versuchen, die Antwort besser zu machen.
quelle
In Ermangelung der Ententypisierung, wie Sie sie in Python verwenden können, ist C # auf Schnittstellen angewiesen, um Abstraktionen bereitzustellen. Wenn die Abhängigkeiten einer Klasse alle konkrete Typen wären, könnten Sie keinen anderen Typ übergeben - mithilfe von Schnittstellen können Sie jeden Typ übergeben, der die Schnittstelle implementiert.
quelle
Das Pizza-Beispiel ist schlecht, weil Sie eine abstrakte Klasse verwenden sollten, die die Bestellung abwickelt, und die Pizzen beispielsweise nur den Pizzatyp überschreiben sollten.
Sie verwenden Schnittstellen, wenn Sie über eine gemeinsam genutzte Eigenschaft verfügen, Ihre Klassen jedoch von verschiedenen Orten erben oder wenn Sie keinen gemeinsamen Code haben, den Sie verwenden könnten. Zum Beispiel werden hier Dinge verwendet, die entsorgt werden
IDisposable
können. Sie wissen, dass sie entsorgt werden. Sie wissen einfach nicht, was passieren wird, wenn sie entsorgt werden.Eine Schnittstelle ist nur ein Vertrag, der Ihnen einige Dinge sagt, die ein Objekt tun kann, welche Parameter und welche Rückgabetypen zu erwarten sind.
quelle
Stellen Sie sich den Fall vor, in dem Sie die Basisklassen nicht kontrollieren oder besitzen.
Nehmen wir zum Beispiel visuelle Steuerelemente in .NET für Winforms, die alle von der Basisklasse Control erben, die vollständig im .NET Framework definiert ist.
Nehmen wir an, Sie erstellen benutzerdefinierte Steuerelemente. Sie möchten neue Schaltflächen, Textfelder, Listenansichten, Raster usw. erstellen und möchten, dass alle über bestimmte Funktionen verfügen, die nur für Ihre Steuerelemente gelten.
Beispielsweise möchten Sie möglicherweise eine allgemeine Methode zum Behandeln von Themen oder eine allgemeine Methode zum Behandeln der Lokalisierung.
In diesem Fall können Sie nicht "nur eine Basisklasse erstellen", da Sie in diesem Fall alles , was sich auf Steuerelemente bezieht, neu implementieren müssen .
Stattdessen steigen Sie von Button, TextBox, ListView, GridView usw. ab und fügen Ihren Code hinzu.
Dies stellt jedoch ein Problem dar. Wie können Sie jetzt identifizieren, welche Steuerelemente "Ihnen" gehören, wie können Sie einen Code erstellen, der besagt, dass "für alle Steuerelemente in dem Formular, das mir gehört, das Thema auf X gesetzt wird".
Schnittstellen eingeben.
Schnittstellen sind eine Möglichkeit, ein Objekt zu betrachten und festzustellen, ob das Objekt einem bestimmten Vertrag entspricht.
Sie würden "YourButton" erstellen, von Button abstammen und Unterstützung für alle benötigten Schnittstellen hinzufügen.
Auf diese Weise können Sie Code wie folgt schreiben:
Dies wäre ohne Schnittstellen nicht möglich, stattdessen müssten Sie Code wie folgt schreiben:
quelle
In diesem Fall könnten (und würden) Sie einfach eine Pizza-Basisklasse definieren und von diesen erben. Es gibt jedoch zwei Gründe, aus denen Sie mit Interfaces Dinge tun können, die auf andere Weise nicht erreicht werden können:
Eine Klasse kann mehrere Schnittstellen implementieren. Es werden nur Funktionen definiert, die die Klasse haben muss. Durch die Implementierung einer Reihe von Schnittstellen kann eine Klasse mehrere Funktionen an verschiedenen Orten erfüllen.
Eine Schnittstelle kann in einem größeren Bereich als die Klasse oder der Aufrufer definiert werden. Dies bedeutet, dass Sie die Funktionalität trennen, die Projektabhängigkeit trennen und die Funktionalität in einem Projekt oder einer Klasse sowie die Implementierung an anderer Stelle beibehalten können.
Eine Implikation von 2 ist, dass Sie die verwendete Klasse ändern können, nur dass die entsprechende Schnittstelle implementiert werden muss.
quelle
Bedenken Sie, dass Sie in C # keine Mehrfachvererbung verwenden können, und sehen Sie sich Ihre Frage erneut an.
quelle
Schnittstelle = Vertrag, wird für lose Kopplung verwendet (siehe GRASP ).
quelle
Wenn ich an einer API zum Zeichnen von Formen arbeite, möchte ich möglicherweise DirectX- oder Grafikaufrufe oder OpenGL verwenden. Also werde ich eine Schnittstelle erstellen, die meine Implementierung von dem abstrahiert, was Sie aufrufen.
Sie rufen also eine Factory-Methode auf :
MyInterface i = MyGraphics.getInstance()
. Dann haben Sie einen Vertrag, damit Sie wissen, mit welchen Funktionen Sie rechnen könnenMyInterface
. Sie können also aufrufeni.drawRectangle
oderi.drawCube
wissen, dass die Funktionen unterstützt werden, wenn Sie eine Bibliothek gegen eine andere austauschen.Dies wird wichtiger, wenn Sie Dependency Injection verwenden, da Sie dann in einer XML-Datei Implementierungen austauschen können.
Möglicherweise haben Sie eine Kryptobibliothek, die exportiert werden kann und für den allgemeinen Gebrauch bestimmt ist, und eine andere, die nur an amerikanische Unternehmen verkauft werden kann. Der Unterschied besteht darin, dass Sie eine Konfigurationsdatei ändern und der Rest des Programms nicht geändert.
Dies wird häufig bei Sammlungen in .NET verwendet, da Sie beispielsweise nur
List
Variablen verwenden sollten und sich keine Sorgen machen sollten, ob es sich um eine ArrayList oder eine LinkedList handelt.Solange Sie auf der Schnittstelle codieren, kann der Entwickler die tatsächliche Implementierung ändern und der Rest des Programms bleibt unverändert.
Dies ist auch beim Komponententest hilfreich, da Sie ganze Schnittstellen verspotten können. Ich muss also nicht zu einer Datenbank gehen, sondern zu einer verspotteten Implementierung, die nur statische Daten zurückgibt, damit ich meine Methode testen kann, ohne mir Sorgen machen zu müssen Die Datenbank ist wegen Wartungsarbeiten nicht verfügbar oder nicht.
quelle
Eine Schnittstelle ist wirklich ein Vertrag, dem die implementierenden Klassen folgen müssen. Sie ist in der Tat die Basis für so ziemlich jedes mir bekannte Entwurfsmuster.
In Ihrem Beispiel wird die Schnittstelle erstellt, da dann garantiert alles, was IS A Pizza ist, dh die Pizza-Schnittstelle implementiert, implementiert wurde
Nach Ihrem erwähnten Code könnten Sie so etwas haben:
Auf diese Weise verwenden Sie Polymorphismus und alles, was Sie interessiert, ist, dass Ihre Objekte auf order () reagieren.
quelle
Ich habe auf dieser Seite nach dem Wort "Komposition" gesucht und es kein einziges Mal gesehen. Diese Antwort ist sehr viel zusätzlich zu den oben genannten Antworten.
Einer der absolut entscheidenden Gründe für die Verwendung von Schnittstellen in einem objektorientierten Projekt besteht darin, dass Sie die Komposition der Vererbung vorziehen können. Durch die Implementierung von Schnittstellen können Sie Ihre Implementierungen von den verschiedenen Algorithmen entkoppeln, die Sie auf sie anwenden.
Dieses hervorragende "Decorator Pattern" -Tutorial von Derek Banas (das - komischerweise - auch Pizza als Beispiel verwendet) ist eine lohnende Illustration:
https://www.youtube.com/watch?v=j40kRwSm4VE
quelle
Schnittstellen dienen zum Anwenden einer Verbindung zwischen verschiedenen Klassen. Zum Beispiel haben Sie eine Klasse für Auto und einen Baum;
Sie möchten eine brennbare Funktionalität für beide Klassen hinzufügen. Aber jede Klasse hat ihre eigenen Möglichkeiten zu brennen. so machst du einfach;
quelle
Sie erhalten Schnittstellen, wenn Sie sie benötigen :) Sie können Beispiele studieren, aber Sie benötigen die Aha! Effekt, um sie wirklich zu bekommen.
Jetzt, da Sie wissen, was Schnittstellen sind, codieren Sie einfach ohne sie. Früher oder später werden Sie auf ein Problem stoßen, bei dem die Verwendung von Schnittstellen am natürlichsten ist.
quelle
Ich bin überrascht, dass nicht viele Beiträge den wichtigsten Grund für eine Benutzeroberfläche enthalten: Design Patterns . Es ist das Gesamtbild bei der Verwendung von Verträgen, und obwohl es sich um eine Syntaxdekoration für Maschinencode handelt (um ehrlich zu sein, ignoriert der Compiler sie wahrscheinlich nur), sind Abstraktion und Schnittstellen für OOP, menschliches Verständnis und komplexe Systemarchitekturen von entscheidender Bedeutung.
Erweitern wir die Pizza-Analogie zu einem 3-Gänge-Menü. Wir haben weiterhin die
Prepare()
Kernschnittstelle für alle unsere Lebensmittelkategorien, aber wir hätten auch abstrakte Erklärungen für die Kursauswahl (Vorspeise, Hauptgericht, Dessert) und unterschiedliche Eigenschaften für Lebensmittelarten (herzhaft / süß, vegetarisch / nicht vegetarisch, glutenfrei etc).Basierend auf diesen Spezifikationen könnten wir das Abstract Factory- Muster implementieren , um den gesamten Prozess zu konzipieren, aber Schnittstellen verwenden, um sicherzustellen, dass nur die Grundlagen konkret sind. Alles andere könnte flexibel werden oder den Polymorphismus fördern, jedoch die Kapselung zwischen den verschiedenen Klassen der
Course
Implementierung derICourse
Schnittstelle beibehalten .Wenn ich mehr Zeit hätte, würde ich gerne ein vollständiges Beispiel dafür erstellen, oder jemand kann dies für mich erweitern, aber zusammenfassend wäre eine C # -Schnittstelle das beste Werkzeug beim Entwerfen dieses Systemtyps.
quelle
Hier ist eine Schnittstelle für Objekte mit rechteckiger Form:
Sie müssen lediglich Möglichkeiten implementieren, um auf die Breite und Höhe des Objekts zuzugreifen.
Definieren wir nun eine Methode, die für jedes Objekt funktioniert
IRectangular
:Dadurch wird die Fläche eines rechteckigen Objekts zurückgegeben.
Implementieren wir eine Klasse
SwimmingPool
, die rechteckig ist:Und noch eine Klasse
House
, die ebenfalls rechteckig ist:Vorausgesetzt, Sie können die anrufen
Area
Methode für Häuser oder Schwimmbäder :Auf diese Weise können Ihre Klassen das Verhalten (statische Methoden) von einer beliebigen Anzahl von Schnittstellen "erben".
quelle
Eine Schnittstelle definiert einen Vertrag zwischen dem Anbieter einer bestimmten Funktionalität und den entsprechenden Verbrauchern. Es entkoppelt die Implementierung vom Vertrag (Schnittstelle). Sie sollten sich objektorientierte Architektur und Design ansehen. Vielleicht möchten Sie mit Wikipedia beginnen: http://en.wikipedia.org/wiki/Interface_(computing)
quelle
Was?
Schnittstellen sind grundsätzlich ein Vertrag, den alle Klassen implementieren folgen sollten, die Schnittstelle . Sie sehen aus wie eine Klasse, haben aber keine Implementierung.
In
C#
Schnittstellennamen wird die Konvention durch Präfixieren eines 'I' definiert. Wenn Sie also eine Schnittstelle namens Shapes haben möchten, deklarieren Sie diese alsIShapes
Jetzt, warum ?
Improves code re-usability
Können sagen , Sie zeichnen möchten
Circle
,Triangle.
Sie können sie gruppieren und nennen sieShapes
und haben Methoden zu ziehenCircle
undTriangle
konkrete Umsetzung aber mit morgen eine schlechte Idee wäre, weil du 2 mehr haben könnte entscheidenShapes
Rectangle
&Square
. Wenn Sie sie jetzt hinzufügen, besteht eine große Wahrscheinlichkeit, dass Sie andere Teile Ihres Codes beschädigen.Mit Interface isolieren Sie die unterschiedliche Implementierung vom Vertrag
Live-Szenario Tag 1
Sie wurden gebeten, eine App zum Zeichnen von Kreisen und Dreiecken zu erstellen
Live-Szenario Tag 2
Wenn Sie zum Hinzufügen
Square
und Hinzufügen aufgefordertRectangle
wurden, müssen Sie lediglich die Implementierung fürclass Square: IShapes
und inMain
der Liste hinzufügen hinzufügenshapes.Add(new Square());
quelle
Hier gibt es viele gute Antworten, aber ich würde es gerne aus einer etwas anderen Perspektive versuchen.
Sie sind möglicherweise mit den SOLID-Prinzipien des objektorientierten Designs vertraut. Zusammenfassend:
S - Prinzip der Einzelverantwortung O - Prinzip der offenen / geschlossenen Verantwortung L - Prinzip der Liskov-Substitution I - Prinzip der Schnittstellentrennung D - Prinzip der Abhängigkeitsinversion
Das Befolgen der SOLID-Prinzipien hilft dabei, Code zu erstellen, der sauber, gut faktorisiert, zusammenhängend und lose gekoppelt ist. Vorausgesetzt, dass:
Dann ist alles, was beim Abhängigkeitsmanagement hilft, ein großer Gewinn. Schnittstellen und das Prinzip der Abhängigkeitsinversion helfen wirklich dabei, Code von Abhängigkeiten von konkreten Klassen zu entkoppeln, sodass Code eher in Bezug auf Verhalten als in Bezug auf Implementierungen geschrieben und begründet werden kann . Dies hilft dabei, den Code in Komponenten zu unterteilen, die zur Laufzeit und nicht zur Kompilierungszeit erstellt werden können, und bedeutet auch, dass diese Komponenten ganz einfach ein- und ausgesteckt werden können, ohne dass der Rest des Codes geändert werden muss.
Schnittstellen helfen insbesondere beim Prinzip der Abhängigkeitsinversion, bei dem Code in eine Sammlung von Diensten unterteilt werden kann, wobei jeder Dienst durch eine Schnittstelle beschrieben wird. Services können dann zur Laufzeit in Klassen "eingefügt" werden, indem sie als Konstruktorparameter übergeben werden. Diese Technik wird wirklich kritisch, wenn Sie anfangen, Komponententests zu schreiben und testgetriebene Entwicklung zu verwenden. Versuch es! Sie werden schnell verstehen, wie Schnittstellen dazu beitragen, den Code in verwaltbare Blöcke aufzuteilen, die einzeln isoliert getestet werden können.
quelle
Der Hauptzweck der Schnittstellen besteht darin, dass zwischen Ihnen und jeder anderen Klasse, die diese Schnittstelle implementiert, ein Vertrag geschlossen wird, der Ihren Code entkoppelt und Erweiterbarkeit ermöglicht.
quelle
Therese sind wirklich gute Beispiele gefragt.
Zum anderen müssen Sie im Fall einer switch-Anweisung nicht mehr jedes Mal warten und wechseln, wenn rio eine Aufgabe auf eine bestimmte Weise ausführen soll.
Wenn Sie in Ihrem Pizza-Beispiel eine Pizza machen möchten, ist die Benutzeroberfläche alles, was Sie brauchen. Von dort aus kümmert sich jede Pizza um ihre eigene Logik.
Dies hilft, die Kopplung und die zyklomatische Komplexität zu reduzieren. Sie müssen die Logik noch implementieren, aber es gibt weniger, die Sie im Gesamtbild im Auge behalten müssen.
Für jede Pizza können Sie dann die für diese Pizza spezifischen Informationen verfolgen. Was andere Pizzen haben, spielt keine Rolle, denn nur die anderen Pizzen müssen es wissen.
quelle
Der einfachste Weg, über Schnittstellen nachzudenken, besteht darin, zu erkennen, was Vererbung bedeutet. Wenn die Klasse CC die Klasse C erbt, bedeutet dies Folgendes:
Diese beiden Funktionen der Vererbung sind in gewissem Sinne unabhängig; Obwohl die Vererbung beide gleichzeitig gilt, ist es auch möglich, die zweite ohne die erste anzuwenden. Dies ist nützlich, da es viel komplizierter ist, einem Objekt zu erlauben, Mitglieder von zwei oder mehr nicht verwandten Klassen zu erben, als zuzulassen, dass ein Typ mehrere Dinge durch mehrere Typen ersetzt.
Eine Schnittstelle ähnelt einer abstrakten Basisklasse, hat jedoch einen wesentlichen Unterschied: Ein Objekt, das eine Basisklasse erbt, kann keine andere Klasse erben. Im Gegensatz dazu kann ein Objekt eine Schnittstelle implementieren, ohne seine Fähigkeit zu beeinträchtigen, eine gewünschte Klasse zu erben oder andere Schnittstellen zu implementieren.
Ein nettes Merkmal davon (im .net-Framework, IMHO, nicht ausreichend genutzt) ist, dass es möglich ist, deklarativ anzugeben, was ein Objekt tun kann. Einige Objekte möchten beispielsweise ein Datenquellenobjekt, von dem sie Dinge per Index abrufen können (wie dies mit einer Liste möglich ist), müssen dort jedoch nichts speichern. Andere Routinen benötigen ein Daten-Depot-Objekt, in dem sie Dinge nicht nach Index speichern können (wie bei Collection.Add), aber sie müssen nichts zurücklesen. Einige Datentypen ermöglichen den Zugriff über den Index, das Schreiben jedoch nicht. andere erlauben das Schreiben, erlauben aber nicht den Zugriff per Index. Einige erlauben natürlich beides.
Wenn ReadableByIndex und Appendable nicht verwandte Basisklassen wären, wäre es unmöglich, einen Typ zu definieren, der sowohl an Dinge übergeben werden könnte, die einen ReadableByIndex erwarten, als auch an Dinge, die einen Appendable erwarten. Man könnte versuchen, dies zu mildern, indem ReadableByIndex oder Appendable vom anderen abgeleitet werden. Die abgeleitete Klasse müsste öffentliche Mitglieder für beide Zwecke zur Verfügung stellen, warnt jedoch davor, dass einige öffentliche Mitglieder möglicherweise nicht tatsächlich funktionieren. Einige Klassen und Schnittstellen von Microsoft tun dies, aber das ist ziemlich schwierig. Ein sauberer Ansatz besteht darin, Schnittstellen für die verschiedenen Zwecke zu haben und dann Objekte Schnittstellen für die Dinge implementieren zu lassen, die sie tatsächlich tun können. Wenn eine Schnittstelle IReadableByIndex und eine andere Schnittstelle IAppendable hatte,
quelle
Schnittstellen können auch verkettet werden, um eine weitere Schnittstelle zu erstellen. Diese Fähigkeit, mehrere Schnittstellen zu implementieren, bietet dem Entwickler den Vorteil, seinen Klassen Funktionen hinzuzufügen, ohne die aktuelle Klassenfunktionalität ändern zu müssen (SOLID-Prinzipien).
O = "Klassen sollten zur Erweiterung geöffnet, aber zur Änderung geschlossen sein"
quelle
Für mich ist ein Vorteil / Vorteil einer Schnittstelle, dass sie flexibler ist als eine abstrakte Klasse. Da Sie nur eine abstrakte Klasse erben können, aber mehrere Schnittstellen implementieren können, werden Änderungen an einem System, das an vielen Stellen eine abstrakte Klasse erbt, problematisch. Wenn es an 100 Stellen vererbt wird, erfordert eine Änderung Änderungen an allen 100. Mit der Schnittstelle können Sie die neue Änderung jedoch in eine neue Schnittstelle einfügen und diese Schnittstelle nur dort verwenden, wo sie benötigt wird (Schnittstellenfolge von SOLID). Darüber hinaus scheint die Speichernutzung bei der Schnittstelle geringer zu sein, da ein Objekt im Schnittstellenbeispiel nur einmal im Speicher verwendet wird, obwohl an vielen Stellen die Schnittstelle implementiert ist.
quelle
Schnittstellen werden verwendet, um die Konsistenz auf eine Weise zu fördern, die lose gekoppelt ist, wodurch sie sich von abstrakten Klassen unterscheidet, die eng gekoppelt sind. Deshalb wird sie auch allgemein als Vertrag definiert. Welche Klassen, die die Schnittstelle implementieren, halten sich an "Regeln / Syntax" durch die Schnittstelle definiert und es gibt keine konkreten Elemente darin.
Ich werde nur ein Beispiel geben, das von der folgenden Grafik unterstützt wird.
Stellen Sie sich vor, in einer Fabrik gibt es drei Arten von Maschinen. Eine Rechteckmaschine, eine Dreiecksmaschine und eine Polygonmaschine. Die Zeiten sind wettbewerbsfähig und Sie möchten die Bedienerschulung optimieren. Sie möchten sie nur in einer Methode zum Starten und Stoppen von Maschinen trainieren haben eine grüne Starttaste und eine rote Stopptaste. So haben Sie jetzt auf 3 verschiedenen Maschinen eine konsistente Möglichkeit, 3 verschiedene Maschinentypen zu starten und zu stoppen. Stellen Sie sich nun vor, diese Maschinen sind Klassen und die Klassen müssen Start- und Stoppmethoden haben, wie Sie Wird die Konsistenz zwischen diesen Klassen gesteigert, die sehr unterschiedlich sein können? Schnittstelle ist die Antwort.
Als einfaches Beispiel zur Visualisierung könnte man sich fragen, warum man nicht die abstrakte Klasse verwendet. Mit einer Schnittstelle müssen die Objekte nicht direkt miteinander verknüpft oder vererbt werden, und Sie können dennoch die Konsistenz über verschiedene Klassen hinweg verbessern.
quelle
Lassen Sie mich dies aus einer anderen Perspektive beschreiben. Lassen Sie uns eine Geschichte nach dem Beispiel erstellen, das ich oben gezeigt habe;
Programm, Maschine und IMachine sind die Akteure unserer Geschichte. Das Programm möchte ausgeführt werden, verfügt jedoch nicht über diese Fähigkeit, und die Maschine weiß, wie es ausgeführt wird. Machine und IMachine sind beste Freunde, aber Program spricht nicht mit Machine. Also machen Program und IMachine einen Deal und beschlossen, dass IMachine Program mitteilen wird, wie es ausgeführt werden soll, indem sie Machine (wie ein Reflektor) aussehen.
Und Program lernt mithilfe von IMachine, wie man läuft.
Die Schnittstelle bietet Kommunikation und die Entwicklung lose gekoppelter Projekte.
PS: Ich habe die Methode der konkreten Klasse als privat. Mein Ziel hier ist es, eine lose Kopplung zu erreichen, indem der Zugriff auf konkrete Klasseneigenschaften und -methoden verhindert wird und nur die Möglichkeit besteht, sie über Schnittstellen zu erreichen. (Also habe ich die Methoden der Schnittstellen explizit definiert).
quelle
Ich weiß, dass ich sehr spät bin .. (fast neun Jahre), aber wenn jemand eine kleine Erklärung will, dann können Sie dies tun:
Mit einfachen Worten, Sie verwenden die Schnittstelle, wenn Sie wissen, was ein Objekt kann oder welche Funktion wir für ein Objekt implementieren werden. Beispiel Einfügen, Aktualisieren und Löschen.
Wichtiger Hinweis: Schnittstellen sind IMMER öffentlich.
Hoffe das hilft.
quelle