Ich habe eine Processor
Zahlenklasse, die zwei sehr unterschiedliche Dinge tut, aber vom gemeinsamen Code aufgerufen wird (eine "Umkehrung der Kontrolle" -Situation).
Ich frage mich, welche Designüberlegungen ich berücksichtigen sollte (oder für Sie US-Benutzer berücksichtigen sollte), wenn ich entscheide, ob sie alle von einer Schnittstelle erben BaseProcessor
oder IProcessor
als Schnittstelle implementieren sollen .
c#
oop
design-patterns
inheritance
interface
wahrscheinlich am Strand
quelle
quelle
Antworten:
Im Allgemeinen lautet die Regel ungefähr so:
Um dies etwas konkreter auszudrücken, schauen wir uns ein Beispiel an. Die
System.Drawing.Bitmap
Klasse ist-ein Bild (und als solche, es erbt von derImage
Klasse), aber es auch Can-do entsorgen, so dass es die implementiertIDisposable
Schnittstelle. Es kann auch Serialisierung durchführen, sodass es über dieISerializable
Schnittstelle implementiert wird.In der Praxis werden Schnittstellen jedoch häufig verwendet, um die Mehrfachvererbung in C # zu simulieren. Wenn Ihre
Processor
Klasse von so etwas erben mussSystem.ComponentModel.Component
, haben Sie keine andere Wahl, als eineIProcessor
Schnittstelle zu implementieren .Tatsache ist, dass sowohl Schnittstellen als auch abstrakte Basisklassen einen Vertrag bereitstellen, der angibt, was eine bestimmte Klasse tun kann. Es ist ein weit verbreiteter Mythos, dass Schnittstellen erforderlich sind, um diesen Vertrag zu deklarieren, aber das ist nicht korrekt. Der größte Vorteil für mich ist, dass Sie mit abstrakten Basisklassen Standardfunktionen für die Unterklassen bereitstellen können. Wenn es jedoch keine sinnvolle Standardfunktionalität gibt, hindert Sie nichts daran, die Methode selbst als zu
abstract
kennzeichnen, sodass abgeleitete Klassen sie selbst implementieren müssen, genau wie wenn sie eine Schnittstelle implementieren würden.Für Antworten auf Fragen wie diese wende ich mich häufig den .NET Framework-Entwurfsrichtlinien zu , die Folgendes zur Auswahl zwischen Klassen und Schnittstellen enthalten:
Ihre allgemeinen Empfehlungen lauten wie folgt:
Chris Anderson stimmt diesem letzten Grundsatz besonders zu und argumentiert:
quelle
Richard,
Warum zwischen ihnen wählen? Ich hätte eine IProzessor-Schnittstelle als veröffentlichten Typ (zur Verwendung an anderer Stelle im System). und wenn es so kommt, dass Ihre verschiedenen CURRENT-Implementierungen von IProcessor ein gemeinsames Verhalten aufweisen, ist eine abstrakte BaseProcessor-Klasse ein wirklich guter Ort, um dieses gemeinsame Verhalten zu implementieren.
Auf diese Weise muss ein IProzessor, der in Zukunft NICHT für die Dienste von BaseProcessor vorgesehen ist, nicht vorhanden sein (und möglicherweise ausgeblendet werden) in dupliziertem Code / Konzepten.
Nur meine bescheidene MEINUNG.
Prost. Keith.
quelle
Schnittstellen sind ein "Vertrag", der sicherstellt, dass einige Klassen eine gewünschte Gruppe von Mitgliedern implementieren - Eigenschaften, Methoden und Ereignisse -.
Basisklassen (konkret oder abstrakt, spielt keine Rolle) sind der Archetyp einer Entität. Das sind Entitäten, die darstellen, was in einer tatsächlichen physischen oder konzeptuellen Einheit üblich ist.
Wann sind Schnittstellen zu verwenden?
Wann immer ein Typ deklarieren muss, dass er zumindest einige Verhaltensweisen und Eigenschaften aufweist, die ein Verbraucher beachten und verwenden sollte, um eine Aufgabe zu erfüllen.
Wann werden Basisklassen verwendet (konkret und / oder abstrakt)?
Immer wenn eine Gruppe von Entitäten denselben Archetyp hat, bedeutet dies, dass B A erbt, weil B A mit Unterschieden ist, B jedoch als A identifiziert werden kann.
Beispiele:
Reden wir über Tische.
Wir akzeptieren recycelbare Tabellen => Dies muss mit einer Schnittstelle wie "IRecyclableTable" definiert werden, um sicherzustellen, dass alle recycelbaren Tabellen eine "Recycle" -Methode haben .
Wir wollen Desktop-Tabellen => Dies muss mit Vererbung definiert werden. Eine "Desktop-Tabelle" ist eine "Tabelle". Alle Tabellen haben gemeinsame Eigenschaften und Verhaltensweisen, und Desktop-Tabellen haben dieselben Eigenschaften, die solche Dinge hinzufügen, die dazu führen, dass eine Desktop-Tabelle anders funktioniert als andere Tabellentypen.
Ich könnte über Assoziationen sprechen, die Bedeutung beider Fälle in einem Objektgraphen, aber meiner bescheidenen Meinung nach würde ich genau mit dieser Argumentation antworten, wenn ich Argumente aus konzeptioneller Sicht liefern muss.
quelle
IRecyclable
Schnittstelle implementieren , da es wahrscheinlich ist, dass mehrere nicht verwandte Objekte recycelbar sein müssen (dh andere Klassen, die keine Tabellen darstellen). Und dann hätte ich immer noch alle Tabellen aus derTable
Klasse implementiert , einschließlichDesktopTable
undPicnicTable
undFoldingTable
.Ich bin nicht sehr gut in der Auswahl von Designs, aber wenn ich gefragt werde, bevorzuge ich die Implementierung einer iProcessor-Oberfläche, wenn nur Mitglieder erweitert werden müssen. Wenn es andere Funktionen gibt, die nicht erweitert werden müssen, ist das Erben vom Basisprozessor die bessere Option.
quelle
Abgesehen von der Designreinheit können Sie nur einmal erben. Wenn Sie also eine Framework-Klasse erben müssen, ist die Frage umstritten.
Wenn Sie eine Wahl haben, können Sie pragmatisch die Option wählen, die die meisten Eingaben spart. Es ist normalerweise sowieso die puristische Wahl.
BEARBEITEN:
Wenn die Basisklasse eine Implementierung hat, kann dies nützlich sein. Wenn sie rein abstact ist, kann sie auch eine Schnittstelle sein.
quelle
Angesichts der Tatsache, dass die SOLID-Prinzipien Ihrem Projekt mehr Wartbarkeit und Erweiterbarkeit bieten, würde ich Schnittstellen der Vererbung vorziehen.
Wenn Sie Ihrer Schnittstelle "zusätzliche Funktionen" hinzufügen müssen, ist es am besten, eine neue Schnittstelle zu erstellen, die dem I in SOLID folgt, dem Prinzip der Schnittstellensegregation.
quelle
Wenn Sie keine Rolle spielen, wählen Sie immer Schnittstelle. Es ermöglicht mehr Flexibilität. Es könnte Sie vor zukünftigen Änderungen schützen (wenn Sie etwas in der Basisklasse ändern müssen, sind möglicherweise die geerbten Klassen betroffen). Es ermöglicht auch, die Details besser zu kapseln. Wenn Sie eine Inversion der Steuerung der Abhängigkeitsinjektion verwenden, bevorzugen diese tendenziell die Schnittstelle.
Oder wenn eine Vererbung nicht vermieden werden kann, ist es wahrscheinlich eine gute Idee, beide zusammen zu verwenden. Erstellen Sie eine abstrakte Basisklasse, die eine Schnittstelle implementiert. In Ihrem Fall implementiert ProcessorBase einen IProzessor.
Ähnlich wie ControllerBase und IController in ASP.NET Mvc.
quelle