Ich habe eine Klasse mit einigen Standard- / gemeinsamen Funktionen. Ich benutze abstract class
dafür:
public interface ITypeNameMapper
{
string Map(TypeDefinition typeDefinition);
}
public abstract class TypeNameMapper : ITypeNameMapper
{
public virtual string Map(TypeDefinition typeDefinition)
{
if (typeDefinition is ClassDefinition classDefinition)
{
return Map(classDefinition);
}
...
throw new ArgumentOutOfRangeException(nameof(typeDefinition));
}
protected abstract string Map(ClassDefinition classDefinition);
}
Wie Sie sehen, habe ich auch die Schnittstelle ITypeNameMapper
. Ist es sinnvoll, diese Schnittstelle zu definieren, wenn ich bereits eine abstrakte Klasse habe TypeNameMapper
oder abstract class
gerade genug?
TypeDefinition
in diesem minimalen Beispiel ist auch abstrakt.
c#
interfaces
abstract-class
Konrad
quelle
quelle
Antworten:
Ja, da C # die Mehrfachvererbung nur mit Schnittstellen zulässt.
Wenn ich also eine Klasse habe, die sowohl TypeNameMapper als auch SomethingelseMapper ist, kann ich Folgendes tun:
quelle
ICanFly
,ICanRun
,ICanSwim
. Sie müssen 8 Kreaturenklassen erstellen, eine für jede Kombination von Merkmalen (JJJ, JJJ, JJJ, ..., NJJ, NJJ). SRP gibt an, dass Sie jedes Verhalten (Fliegen, Laufen, Schwimmen) einmal implementieren und diese dann auf den 8 Kreaturen wiederverwendbar implementieren. Die Komposition wäre hier sogar noch besser, aber wenn Sie die Komposition für eine Sekunde ignorieren, sollten Sie bereits die Notwendigkeit erkennen, aufgrund von SRP mehrere separate wiederverwendbare Verhalten zu erben / implementieren .interface
. Redundante Implementierungen, die in der Basis sein sollten - die sich unabhängig entwickeln! - Bedingte Logikänderungen, die diesen Müll aus Mangel an Template-Methoden, kaputter Kapselung und nicht existierender Abstraktion antreiben. Mein Favorit: Klassen mit einer Methodeinterface
, von denen jede eine eigene Methode mit nur einer Instanz implementiert . ... usw. usw. und usw.Schnittstellen und abstrakte Klassen dienen verschiedenen Zwecken:
Definiert in Ihrem Beispiel
interface ITypeNameMapper
die Bedürfnisse der Kunden undabstract class TypeNameMapper
bietet keinen Mehrwert.quelle
public
dem Kunden gehört.TypeNameMapper
erhöht den Wert erheblich, da der Implementierer keine gemeinsame Logik schreiben muss.interface
oder nicht, ist nur dann relevant, wenn C # eine vollständige Mehrfachvererbung verbietet.Das gesamte Konzept der Schnittstellen wurde erstellt, um eine Klassenfamilie mit einer gemeinsam genutzten API zu unterstützen.
Dies bedeutet ausdrücklich, dass jede Verwendung einer Schnittstelle impliziert, dass mehr als eine Implementierung ihrer Spezifikation vorhanden ist (oder erwartet wird).
DI-Frameworks haben das Wasser hier getrübt, da viele von ihnen eine Schnittstelle benötigen, obwohl es immer nur eine Implementierung geben wird - für mich ist dies ein unangemessener Aufwand für das, was in den meisten Fällen nur ein komplexeres und langsameres Mittel zum Aufrufen von Neuem ist, aber es es ist was es ist.
Die Antwort liegt in der Frage selbst. Wenn Sie eine abstrakte Klasse haben, bereiten Sie die Erstellung mehrerer abgeleiteter Klassen mit einer gemeinsamen API vor. Somit ist die Verwendung einer Schnittstelle eindeutig gekennzeichnet.
quelle
find ... -exec perl -pi -e ...
möglicherweise geringfügige Kompilierungsfehler behoben. Mein Punkt ist: Der Vorteil, kein (derzeit) nutzloses Ding zu haben, ist viel größer als die möglichen zukünftigen Einsparungen.interface
als Code auszeichnen (Sie sprechen von C # -interface
s, nicht von einer abstrakten Schnittstelle, wie jeder Menge von Klassen / Funktionen / etc) und ihn beenden würden "... aber immer noch ein vollständiges Vielfaches verbieten Erbe." Es gibt keine Notwendigkeit fürinterface
s, wenn Sie MI haben.Wenn wir die Frage explizit beantworten möchten, sagt der Autor: "Ist es sinnvoll, diese Schnittstelle zu definieren, wenn ich bereits eine abstrakte Klasse TypeNameMapper habe oder eine abstrakte Klasse gerade ausreicht?"
Die Antwort lautet "Ja" und "Nein". Ja, Sie sollten die Schnittstelle erstellen, obwohl Sie bereits über die abstrakte Basisklasse verfügen (da Sie in keinem Clientcode auf die abstrakte Basisklasse verweisen sollten), und "Nein", weil Sie keine Basisklasse erstellen sollten die abstrakte Basisklasse, wenn keine Schnittstelle vorhanden ist.
Es ist offensichtlich, dass Sie nicht genügend Gedanken in die API gesteckt haben, die Sie erstellen möchten. Überlegen Sie sich zuerst die API und führen Sie dann bei Bedarf eine teilweise Implementierung durch. Die Tatsache, dass Ihre abstrakte Basisklasse Code eincheckt, reicht aus, um Ihnen mitzuteilen, dass es sich nicht um die richtige Abstraktion handelt.
Wie in den meisten OOD-Situationen informiert Sie Ihr Code darüber, was als Nächstes kommt, wenn Sie sich in der Rille befinden und Ihr Objektmodell gut ausgearbeitet haben. Beginnen Sie mit einer Schnittstelle und implementieren Sie diese Schnittstelle in den Klassen, die Sie benötigen. Wenn Sie feststellen, dass Sie ähnlichen Code schreiben, extrahieren Sie ihn in eine abstrakte Basisklasse - es ist die Schnittstelle, die wichtig ist, die abstrakte Basisklasse ist nur ein Helfer, und es kann mehr als einen geben.
quelle
Dies ist ziemlich schwierig zu beantworten, ohne den Rest der Anwendung zu kennen.
Angenommen, Sie verwenden ein DI-Modell und haben Ihren Code so erweiterbar wie möglich gestaltet (so dass Sie
TypeNameMapper
leicht neue hinzufügen können), würde ich Ja sagen.Gründe für:
interface
, worüber Sie sich bei untergeordneten Implementierungen keine Gedanken machen müsseninterface
s. Obwohl viele von ihnen mit abstrakten Klassen arbeiten, ist dies nicht immer selbstverständlich, und ich bin fest davon überzeugt, dass ich den gleichen Weg wie die anderen 99% der Bibliotheksbenutzer einschlagen werde, wenn dies möglich ist.Gründe gegen:
class
/interface
ändert, entsteht ein wenig zusätzlicher AufwandAlles in allem würde ich sagen, dass Sie das schaffen sollten
interface
. Die Gründe dafür sind vernachlässigbar, aber obwohl es möglich ist, Abstracts in Mocking und IoC zu verwenden, ist es mit Interfaces oft viel einfacher. Letztendlich würde ich jedoch den Code eines Kollegen nicht kritisieren, wenn sie den umgekehrten Weg gehen würden.quelle