Ich bin mir bewusst, dass dies eine sehr grundlegende Frage ist, aber ein Interviewer hat mich sehr trickreich gefragt und ich war hilflos :(
Ich kenne nur materielle oder theoretische Definitionen für eine Schnittstelle und habe sie auch in vielen Projekten implementiert, an denen ich gearbeitet habe. Aber ich verstehe wirklich nicht, warum und wie das nützlich ist.
Ich verstehe auch nichts in der Benutzeroberfläche. dh wir verwenden zum Beispiel
conn.Dispose();
in endlich blockieren. Aber ich sehe nicht, dass die Klasse die IDisposable
interface ( SqlConnection
) -Klasse implementiert oder erbt , meine ich. Ich frage mich, wie ich einfach den Methodennamen aufrufen kann. In der gleichen Sache verstehe ich auch nicht, wie die Dispose-Methode funktioniert, da wir den Funktionskörper mit unserer eigenen Implementierung für alle Schnittstellenmethoden implementieren müssen. Wie werden Schnittstellen als Verträge akzeptiert oder benannt? Diese Fragen gingen mir bis jetzt immer wieder durch den Kopf und ehrlich gesagt habe ich nie einen guten Thread gesehen, der meine Fragen auf eine Weise erklären würde, die ich verstehen kann.
MSDN sieht wie immer sehr beängstigend aus und es ist dort keine einzige Zeile klar ( Leute, bitte entschuldigen Sie, wer sich in der Entwicklung auf hohem Niveau befindet. Ich bin der festen Überzeugung, dass jeder Code oder Artikel den Verstand von jedem erreichen sollte, der ihn sieht, daher, wie viele andere sagen, MSDN ist nicht von Nutzen ).
Der Interviewer sagte:
Er hat 5 Methoden und implementiert diese gerne direkt in der Klasse. Wenn Sie sich jedoch für eine abstrakte Klasse oder Schnittstelle entscheiden müssen, welche wählen Sie und warum? Ich habe ihm all die Dinge beantwortet, die ich in verschiedenen Blogs gelesen habe und die Vor- und Nachteile sowohl der abstrakten Klasse als auch der Schnittstelle sagten, aber er ist nicht überzeugt, er versucht, "Warum Schnittstelle" im Allgemeinen zu verstehen. "Warum abstrakte Klasse" im Allgemeinen, auch wenn ich die gleichen Methoden nur einmal implementieren kann und sie nicht ändern werde.
Ich sehe nirgendwo im Netz, ich könnte einen Artikel bekommen, der mich klar über Schnittstellen und deren Funktionsweise erklärt. Ich bin einer der vielen Programmierer, die noch nichts über Schnittstellen wissen (ich kenne die theoretischen Methoden und Methoden, die ich verwendet habe), aber nicht zufrieden sind, dass ich sie klar verstanden habe.
SqlConnection
erbtSystem.ComponentModel.Component
welche implementiertIDisposable
.SqlConnection
implementiertIDisposable
.Antworten:
Schnittstellen eignen sich hervorragend, wenn Sie so etwas erstellen möchten:
In meinem Beispiel könnte ich ein Entwickler sein, der schreibt
MyLogClass
, und die anderen Entwickler könnten ihre Klassen erstellen, und wenn sie sich protokollieren möchten, implementieren sie die SchnittstelleIMyLogInterface
. Es ist so, als würden sie mich fragen, was sie implementieren müssen, um dieWriteLog()
Methode in zu verwendenMyLogClass
. Die Antwort finden sie in der Oberfläche.quelle
MyClass
undMyOtherClass
warum Sie nicht einfach anrufen,aClass.WriteLog()
warum Sie diesen zusätzlichen Schritt hinzufügen. Die Implementierung vonWriteLog()
würde für jede der Klassen unterschiedlich bleiben, aber Sie haben das Objekt bereits. Warum sollten Sie es also an eine Handlerklasse übergeben?MyLogClass
WriteLog
Methode übergeben. So kann seine Methode mit jedem Objekt umgehen, das implementiert wirdIMyLogInterface
. Hier ist ein weiterer interessanter Beitrag.Ein Grund, warum ich Schnittstellen verwende, ist, dass dies die Flexibilität des Codes erhöht. Angenommen, wir haben eine Methode, die ein Objekt vom Klassentyp Account als Parameter verwendet, z.
Das Problem dabei ist, dass der Methodenparameter auf eine Implementierung eines Kontos festgelegt ist. Dies ist in Ordnung, wenn Sie niemals einen anderen Kontotyp benötigen würden. Nehmen Sie dieses Beispiel, das stattdessen eine Kontoschnittstelle als Parameter verwendet.
Diese Lösung ist nicht auf eine Implementierung festgelegt, was bedeutet, dass ich ihr ein SuperSavingsAccount oder ein ExclusiveAccount (beide implementieren die IAccount-Schnittstelle) übergeben und für jedes implementierte Konto ein anderes Verhalten erhalten kann.
quelle
Schnittstellen sind Verträge, denen Implementierer folgen müssen. Abstrakte Klassen ermöglichen Verträge und gemeinsame Implementierungen - etwas, das Interfaces nicht haben können. Klassen können mehrere Schnittstellen implementieren und erben. Klassen können nur eine einzelne abstrakte Klasse erweitern.
Warum Schnittstelle
IDbCommand
hatSqlCommand
undOracleCommand
implementiert die Schnittstelle auf bestimmte Weise )Warum abstrakt?
quelle
DataContracts
) in einer .NET - Assembly ( zB Contracts.Shared.dll ) , so dass .NET - Client Verbraucher können einfach miteinander arbeiten mitChannelFactory
( Erzeugungscode über Dienstverweis hinzufügen zu vermeiden usw. ) oder mit Service - Referenz mit Gemeinschaftstypen hinzufügenIn diesem Beispiel weiß der PowerSocket also nichts anderes über die anderen Objekte. Die Objekte hängen alle von der vom PowerSocket bereitgestellten Leistung ab, sodass sie IPowerPlug implementieren und auf diese Weise eine Verbindung herstellen 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
Mit einem Wort - wegen Polymorphismus !
Wenn Sie "Auf eine Schnittstelle programmieren, nicht auf eine Implementierung", können Sie verschiedene Objekte, die dieselbe Schnittstelle (denselben Typ) verwenden, als Argument in die Methode einfügen. Auf diese Weise ist Ihr Methodencode nicht mit einer Implementierung einer anderen Klasse gekoppelt, was bedeutet, dass er immer offen ist, um mit neu erstellten Objekten derselben Schnittstelle zu arbeiten. (Öffnungs- / Schließprinzip)
quelle
C # hat keine Ententypisierung - nur weil Sie wissen, dass eine bestimmte Methode in einer Reihe konkreter Klassen implementiert ist, heißt das nicht, dass Sie sie beim Aufrufen dieser Methode alle gleich behandeln können. Durch die Implementierung einer Schnittstelle können Sie alle Klassen, die sie implementieren, hinsichtlich der Definition dieser Schnittstelle als dieselbe Art von Dingen behandeln.
quelle
Ich glaube, dass beim Stellen dieser Fragen bereits viel Blut vergossen wurde, und viele versuchen, dieses Problem zu lösen, indem sie roboterähnliche Begriffe erklären, die kein normaler Mensch verstehen kann.
So zuerst. Um zu erfahren, warum Schnittstelle und warum abstrakt, müssen Sie lernen, wofür sie sind. Ich persönlich habe diese beiden bei der Anwendung der Factory Class gelernt. Unter diesem Link finden Sie eine gute Anleitung
Lassen Sie uns nun auf den Link eingehen, den ich bereits gegeben habe.
Sie haben eine Fahrzeugklasse , die sich je nach Benutzeranforderung ändern kann (z. B. Hinzufügen von LKW , Tank , Flugzeug usw.)
und
und beide haben Vertrag IChoice, die einfach sagen, dass meine Klasse Kaufmethode haben sollte
Sie sehen, diese Schnittstelle erzwingt nur die Methode
Buy()
, lässt jedoch die geerbte Klasse entscheiden, was zu tun ist, wenn sie sie implementiert. Dies ist die Einschränkung von Interface. Wenn Sie nur Interface verwenden, wiederholen Sie möglicherweise eine Aufgabe, die wir mithilfe von abstact automatisch implementieren können. In unserem Beispiel hat der Kauf jedes Fahrzeugs einen Rabatt.Wenn Sie jetzt die Factory-Klasse verwenden, können Sie dasselbe erreichen, aber wenn Sie abstract verwenden, lassen Sie die Basisklasse die
Buy()
Methode ausführen .quelle
Mit einer Schnittstelle können Sie Folgendes tun:
1) Erstellen Sie getrennte Schnittstellen, die unterschiedliche Schnitte Ihrer Implementierung bieten und eine zusammenhängendere Schnittstelle ermöglichen.
2) Lassen Sie mehrere Methoden mit demselben Namen zwischen den Schnittstellen zu, da Sie keine widersprüchliche Implementierung haben, sondern nur eine Signatur.
3) Sie können Ihre Benutzeroberfläche unabhängig von Ihrer Implementierung versionieren und ausgliedern, um sicherzustellen, dass ein Vertrag eingehalten wird.
4) Ihr Code kann sich eher auf Abstraktion als auf Konkretion stützen, was eine intelligente Abhängigkeitsinjektion ermöglicht, einschließlich der Injektion von Test-Mocks usw.
Ich bin mir sicher, dass es noch viele weitere Gründe gibt, dies sind nur einige.
Mit einer abstrakten Klasse können Sie eine teilweise konkrete Basis haben, von der aus Sie arbeiten können. Dies ist nicht dasselbe wie eine Schnittstelle, hat jedoch ihre eigenen Eigenschaften, z. B. die Möglichkeit, eine teilweise Implementierung mithilfe des Template-Methodenmusters zu erstellen.
quelle
Als echtes Beispiel für @ user2211290 Antwort:
Beide
Array
undList
haben SchnittstelleIList
. Unten haben wir einstring[]
und einList<string>
und überprüfen beide mit einer Methode unter Verwendung von IList :quelle
Sie können nur von einer abstrakten Klasse erben. Sie können von mehreren Schnittstellen erben. Dies bestimmt, was ich in den meisten Fällen verwende.
Der Vorteil einer abstrakten Klasse wäre, dass Sie eine Basisimplementierung haben können. Im Fall von IDisposable ist eine Standardimplementierung jedoch nutzlos, da die Basisklasse nicht weiß, wie die Dinge ordnungsgemäß bereinigt werden sollen. Somit wäre eine Schnittstelle besser geeignet.
quelle
Sowohl die abstrakte Klasse als auch die Schnittstelle sind Verträge.
Die Idee eines Vertrags ist, dass Sie ein bestimmtes Verhalten angeben. Wenn Sie sagen, dass Sie implementiert haben, haben Sie dem Vertrag zugestimmt.
Die Wahl von abstrakt über Schnittstelle ist.
Jeder nicht abstrakte Nachkomme der abstrakten Klasse wird den Vertrag implementieren.
gegen
Jede Klasse, die die Schnittstelle implementiert, implementiert den Vertrag.
Sie verwenden also abstrakt, wenn Sie ein Verhalten angeben möchten, das alle Nachkommen implementieren müssen, und sparen sich das Definieren einer separaten Schnittstelle. Jetzt muss alles, was diesen effektiv aggregierten Vertrag erfüllt, ein Nachkomme sein.
quelle
Schnittstellen sollen eine Abstraktion (einen Archetyp) der Abstraktion (der Klassen) der Realität (der Objekte) machen.
Schnittstellen sollen Vertragsbedingungen angeben, ohne die Implementierung durch Klassen bereitzustellen.
Schnittstellen sind Spezifikationen:
Schnittstellen sind Entwurfszeitartefakte, um das unbewegliche Verhalten des Konzepts zu spezifizieren, da es allein und statisch ist.
Klassen sind Artefakte der Implementierungszeit, um die mobile Struktur der Realität während der Interaktion und Bewegung zu spezifizieren.
Was ist eine Schnittstelle?
Wenn Sie eine Katze beobachten, können Sie sagen, dass es sich um ein Tier handelt, das vier Pfoten, einen Kopf, einen Stamm, einen Schwanz und Haare hat. Sie können sehen, dass er laufen, rennen, essen und miauen kann. Und so weiter.
Sie haben gerade eine Schnittstelle mit ihren Eigenschaften und Operationen definiert. Als solche haben Sie keinen Modus operandi definiert, sondern nur Funktionen und Fähigkeiten, ohne zu wissen, wie die Dinge funktionieren: Sie haben Fähigkeiten und Unterscheidungen definiert.
Als solches ist es noch keine Klasse, obwohl wir dies in UML eine Klasse in einem Klassendiagramm nennen, da wir private und geschützte Mitglieder definieren können, um einen tiefen Überblick über das Artefakt zu erhalten. Seien Sie hier nicht verwirrt, denn in UML ist eine Schnittstelle etwas anders als eine Schnittstelle in C #: Sie ist wie ein partieller Zugriffspunkt auf das Abstraktionsatom. Als solches haben wir gesagt, dass eine Klasse mehrere Schnittstellen implementieren kann. Als solches ist es dasselbe, aber nicht, da Schnittstellen in C # sowohl zum Abstrahieren der Abstraktion als auch zum Einschränken dieser Abstraktion als Zugriffspunkt verwendet werden. Es gibt zwei verschiedene Verwendungszwecke. Somit repräsentiert eine Klasse in UML eine vollständige Kopplungsschnittstelle zu einer Programmierklasse, während eine UML-Schnittstelle eine Entkopplungsschnittstelle eines Abschnitts einer Programmierklasse darstellt. Tatsächlich, Das Klassendiagramm in UML kümmert sich nicht um die Implementierung und alle seine Artefakte befinden sich auf der Ebene der Programmierschnittstelle. Während wir UML-Klassen Programmierklassen zuordnen, handelt es sich um eine Umsetzung der abstrakten Abstraktion in eine konkrete Abstraktion. Es gibt eine Subtilität, die die Dichotomie zwischen dem Bereich Design und dem Bereich Programmierung erklärt. Eine Klasse in UML ist also eine Programmierklasse aus der Sicht einer Programmierschnittstelle, während innere verborgene Dinge berücksichtigt werden.
Schnittstellen ermöglichen es auch, Mehrfachvererbung zu simulieren, wenn sie nicht auf unangenehme Weise verfügbar sind. Beispielsweise implementiert die cat-Klasse die cat-Schnittstelle, die sich von der animal-Schnittstelle ableitet. Diese Katzenklasse implementiert auch diese Schnittstellen: Gehen, Laufen, Essen und Geräusche machen. Dies kompensiert das Fehlen einer Mehrfachvererbung auf Klassenebene, aber jedes Mal, wenn Sie alles neu implementieren müssen, können Sie die Realität bestenfalls nicht so faktorisieren, wie es die Realität selbst tut.
Um zu verstehen, dass wir uns auf die Pascal-Objektcodierung beziehen können, definieren Sie in einer Einheit die Schnittstelle und die Implementierungsabschnitte. In der Schnittstelle definieren Sie die Typen und in der Implementierung implementieren Sie den Typ:
Hier stimmt der Schnittstellenabschnitt mit dem UML-Klassendesign überein, während Schnittstellentypen also andere Dinge sind.
In unserem Geschäft haben wir also ein Wort, die Schnittstelle , um zwei unterschiedliche, aber ähnliche Dinge zu benennen, und dies ist eine Quelle der Verwirrung.
Auch in C # können Programmierschnittstellen beispielsweise das Fehlen eines echten generischen Polymorphismus bei offenen Typen kompensieren, ohne das Ziel wirklich zu erreichen, da Sie die stark typisierte Habilität verloren haben.
Schließlich sind Schnittstellen erforderlich, damit inkompatible Systeme kommunizieren können, ohne sich um die Implementierung und Verwaltung von Objekten im Speicher kümmern zu müssen, wie sie mit dem (verteilten) Common Object Model eingeführt wurden.
Was ist eine Klasse?
Nachdem Sie eine Reduktion der Realität von außen definiert haben, können Sie sie aus einer inneren Perspektive beschreiben: In dieser Klasse definieren Sie Datenverarbeitung und Nachrichtenverwaltung, damit die von Ihnen gekapselte Realität zum Leben erweckt wird und dank interagiert zu Objekten mit Instanzen.
In UML realisieren Sie also ein fraktales Eintauchen in die Räder der Maschinerie und beschreiben die Zustände, die Wechselwirkungen usw., um die Abstraktion des Fragments der Realität, mit der Sie umgehen möchten, implementieren zu können.
Als solche ist eine abstrakte Klasse aus Sicht des Compilers irgendwie das Äquivalent einer Schnittstelle.
Mehr Informationen
Protokoll (objektorientierte Programmierung)
C # - Schnittstellen
C # - Klassen
quelle
Lassen Sie mich Ihnen von fliegenden Toastern erzählen.
Es gibt natürlich viele Situationen, in denen Sie ein funktionierendes Softwaresystem erstellen können, ohne Schnittstellen zu deklarieren oder zu implementieren: Jedes objektorientierte Software-Design kann nur mit Klassen realisiert werden.
Andererseits kann jedes Softwaresystem auch in Assemblersprache oder besser noch in Maschinencode implementiert werden. Der Grund, warum wir Abstraktionsmechanismen verwenden, liegt darin, dass sie dazu neigen, die Dinge einfacher zu machen. Schnittstellen sind ein solcher Abstraktionsmechanismus.
Es kommt also einfach so vor, dass es bestimmte nicht triviale objektorientierte Designs gibt, die so viel einfacher zu implementieren sind, wenn Sie Schnittstellen verwenden, dass Schnittstellen in diesen Fällen praktisch notwendig werden.
Diese nicht trivialen Entwürfe haben mit Mehrfachvererbung zu tun, was in ihrer "wahren" Form der Fall ist, wenn eine Klasse nicht nur von einer Basisklasse, sondern von zwei oder mehr Basisklassen erbt. Diese wahre Form ist in C # nicht möglich, aber bevor Sprachen wie C # und Java ins Leben gerufen wurden, war die maßgebliche Sprache C ++, das die echte Mehrfachvererbung vollständig unterstützt. Leider stellte sich heraus, dass eine echte Mehrfachvererbung keine sehr gute Idee ist, da sie das Design der Sprache immens kompliziert und auch verschiedene Probleme aufwirft, zum Beispiel das berühmte "Diamantproblem". (Siehe "Was ist das genaue Problem bei der Mehrfachvererbung?" Antwort von J Francis )
Wenn also jemand eine "fliegende Toaster" -Klasse bauen wollte, würde er von einer vorhandenen "Toaster" -Klasse und auch von einer vorhandenen "fliegenden" Klasse erben. Das Problem, auf das sie wahrscheinlich stießen, war, dass die Stromversorgung der Toasterklasse wahrscheinlich eine Steckdose war, während die Stromversorgung der Flugmaschinenklasse wahrscheinlich Taubenfutter war, und die daraus resultierende neue Klasse auch irgendwie beides haben, oder es wäre unklar, welches es haben würde. (Das Diamantproblem.)
Die Entwickler von Sprachen wie C # und Java haben beschlossen, keine echte Mehrfachvererbung zuzulassen, um die Sprache einfach zu halten und Fallstricke wie das Diamond-Problem zu vermeiden. Es ist jedoch immer noch eine Form der Mehrfachvererbung erforderlich (oder zumindest sehr wünschenswert), sodass in diesen Sprachen Schnittstellen eingeführt wurden, um eine geringere Form der Mehrfachvererbung zu unterstützen und gleichzeitig die Probleme und die Komplexität einer echten Mehrfachvererbung zu vermeiden.
In dieser kleineren Form der Mehrfachvererbung dürfen Sie keine Klasse haben, die von mehr als einer Basisklasse erbt, aber Sie können zumindest von einer oder mehreren Schnittstellen erben. Wenn Sie also einen fliegenden Toaster bauen möchten, können Sie nicht sowohl von einer vorhandenen Toasterklasse als auch von einer vorhandenen fliegenden Klasse erben. Sie können jedoch von einer vorhandenen Toasterklasse erben und dann auch eine fliegende Schnittstelle verfügbar machen, die Sie selbst implementieren. möglicherweise mit den Mitteln, die Sie bereits von Toaster geerbt haben.
Wenn Sie also nie das Bedürfnis haben, eine Klasse zu erstellen, die zwei verschiedene und nicht verwandte Funktionssätze zusammenfasst, benötigen Sie keine Form der Mehrfachvererbung, sodass Sie keine Schnittstellen deklarieren oder implementieren müssen.
quelle
Mithilfe von Schnittstellen kann der Klassenentwickler die verfügbaren Methoden für den Endbenutzer sehr deutlich machen. Sie sind auch ein wesentlicher Bestandteil des Polymorphismus.
quelle
Ich werde die Definition einer Schnittstelle für eine abstrakte Klasse nicht veröffentlichen, da ich denke, dass Sie die Theorie sehr gut kennen und ich gehe davon aus, dass Sie die SOLID-Prinzipien kennen, also lassen Sie uns praktisch werden.
Wie Sie wissen, können Schnittstellen keinen Code enthalten, sodass die Nachteile recht einfach zu verstehen sind.
Wenn Sie die Eigenschaft Ihrer Klasse initialisieren müssen, die einen Konstruktor bereitstellt, oder einen Teil der Implementierung bereitstellen möchten, passt eine abstrakte Klasse gut zu einer Schnittstelle, die dies nicht zulässt.
Im Allgemeinen sollten Sie abstrakte Klassen Schnittstellen vorziehen, wenn Sie dem Client einen Konstruktor oder Code bereitstellen müssen, der Ihre Klasse erbt / erweitert
quelle
Abstrakte Klassen werden für verwandte Entitäten erstellt, wobei Schnittstellen für nicht verwandte Entitäten verwendet werden können.
Zum Beispiel, wenn ich zwei Entitäten habe, die Tier und Mensch sagen, dann werde ich mich für Interface entscheiden, wo, als ob ich im Detail Tiger, Löwe sagen möchte und mit Tier in Beziehung treten möchte, dann Tier abstrakte Klasse wählen werde.
wird wie unten aussehen
quelle