Wenn wir davon ausgehen, dass es nicht wünschenswert ist, dass die Basisklasse eine reine Schnittstellenklasse ist, und die beiden folgenden Beispiele verwenden, was ist ein besserer Ansatz unter Verwendung der abstrakten oder virtuellen Methodenklassendefinition?
Der Vorteil der "abstrakten" Version besteht darin, dass sie wahrscheinlich sauberer aussieht und die abgeleitete Klasse zu einer hoffentlich sinnvollen Implementierung zwingt.
Der Vorteil der "virtuellen" Version besteht darin, dass sie leicht von anderen Modulen abgerufen und zum Testen verwendet werden kann, ohne dass eine Reihe von zugrunde liegenden Frameworks hinzugefügt werden müssen, wie es die abstrakte Version erfordert.
Abstrakte Version:
public abstract class AbstractVersion
{
public abstract ReturnType Method1();
public abstract ReturnType Method2();
.
.
public abstract ReturnType MethodN();
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
Virtuelle Version:
public class VirtualVersion
{
public virtual ReturnType Method1()
{
return ReturnType.NotImplemented;
}
public virtual ReturnType Method2()
{
return ReturnType.NotImplemented;
}
.
.
public virtual ReturnType MethodN()
{
return ReturnType.NotImplemented;
}
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
return ReturnType.NotImplemented
? Ernsthaft? Wenn Sie nicht unimplemented Typ zum Zeitpunkt der Kompilierung ablehnen können (Sie können, verwenden abstrakte Methoden) zumindest werfen eine Ausnahme.Antworten:
Meine Stimme, wenn ich Ihre Sachen konsumieren würde, wäre für die abstrakten Methoden. Das geht einher mit "früh scheitern". Es mag zur Deklarationszeit schwierig sein, alle Methoden hinzuzufügen (obwohl jedes anständige Refactoring-Tool dies schnell erledigt), aber zumindest weiß ich sofort, wo das Problem liegt, und behebe es. Ich würde das lieber tun, als 6 Monate und Änderungen von 12 Personen später zu debuggen, um zu sehen, warum wir plötzlich eine nicht implementierte Ausnahme bekommen.
quelle
Die virtuelle Version ist sowohl fehleranfällig als auch semantisch falsch.
In der Zusammenfassung heißt es: "Diese Methode ist hier nicht implementiert. Sie müssen sie implementieren, damit diese Klasse funktioniert."
Virtual sagt: "Ich habe eine Standardimplementierung, aber Sie können mich bei Bedarf ändern."
Wenn Ihr oberstes Ziel die Testbarkeit ist, sind Schnittstellen normalerweise die beste Option. (Diese Klasse macht x anstatt diese Klasse ist ax). Möglicherweise müssen Sie Ihre Klassen in kleinere Komponenten aufteilen, damit dies gut funktioniert.
quelle
Dies hängt von der Verwendung Ihrer Klasse ab.
Wenn die Methoden eine vernünftige „leere“ Implementierung haben, Sie viele Methoden haben und oft nur einige davon überschreiben, ist die Verwendung von
virtual
Methoden sinnvoll. Zum BeispielExpressionVisitor
wird auf diese Weise implementiert.Ansonsten denke ich, sollten Sie
abstract
Methoden verwenden.Idealerweise sollten Sie keine Methoden haben, die nicht implementiert sind, aber in einigen Fällen ist dies der beste Ansatz. Wenn Sie sich jedoch dazu entschließen, sollten solche Methoden
NotImplementedException
einen speziellen Wert zurückgeben und nicht zurückgeben.quelle
Ich würde vorschlagen, dass Sie überlegen, eine separate Schnittstelle zu definieren, die Ihre Basisklasse implementiert, und dann dem abstrakten Ansatz folgen.
Bildcode wie folgt:
Dies löst diese Probleme:
Wenn der gesamte Code, der von AbstractVersion abgeleitete Objekte verwendet, jetzt implementiert werden kann, um stattdessen die IVersion-Schnittstelle zu empfangen, bedeutet dies, dass sie einfacher in Einheiten getestet werden können.
Release 2 Ihres Produkts kann dann eine Schnittstelle IVersion2 implementieren, um zusätzliche Funktionen bereitzustellen, ohne den Code Ihres bestehenden Kunden zu beschädigen.
z.B.
Es lohnt sich auch, sich über die Abhängigkeitsinversion zu informieren, um zu verhindern, dass diese Klasse fest codierte Abhängigkeiten enthält, die effektive Komponententests verhindern.
quelle
Die Abhängigkeitsinjektion basiert auf Schnittstellen. Hier ist ein kurzes Beispiel. Class Student verfügt über eine Funktion namens CreateStudent, für die ein Parameter erforderlich ist, der die Schnittstelle "IReporting" (mit einer ReportAction-Methode) implementiert. Nachdem ein Schüler erstellt wurde, ruft er ReportAction für den konkreten Klassenparameter auf. Wenn das System so eingerichtet ist, dass nach dem Erstellen eines Schülers eine E-Mail gesendet wird, senden wir eine konkrete Klasse, die in der ReportAction-Implementierung eine E-Mail sendet, oder eine andere konkrete Klasse, die in der ReportAction-Implementierung eine Ausgabe an einen Drucker sendet. Ideal für die Wiederverwendung von Code.
quelle