it-swarm.com.de

Optionale Parameter oder überladene Konstruktoren

Ich implementiere ein DelegateCommand und als ich den/die Konstruktor (en) implementieren wollte, hatte ich die folgenden zwei Entwurfsoptionen:

1: Mehrere überladene Konstruktoren

public DelegateCommand(Action<T> execute) : this(execute, null) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

2: Nur einen Konstruktor mit einem optionalen Parameter

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

Ich weiß nicht, welche ich verwenden soll, weil ich nicht weiß, was mögliche Vor-/Nachteile mit einer der beiden vorgeschlagenen Möglichkeiten kommen. Beide können so genannt werden:

var command = new DelegateCommand(this.myExecute);
var command2 = new DelegateCommand(this.myExecute, this.myCanExecute);

Kann mich bitte jemand in die richtige Richtung weisen und Feedback geben?

28
Thomas Flinkow

Ich bevorzuge mehrere Konstruktoren gegenüber Standardwerten und persönlich mag ich Ihr Beispiel mit zwei Konstruktoren nicht, es sollte unterschiedlich implementiert werden.

Der Grund für die Verwendung mehrerer Konstruktoren besteht darin, dass der Hauptkonstruktor nur prüfen kann, ob alle Parameter nicht null sind und ob sie gültig sind, während andere Konstruktoren Standardwerte für den Hauptkonstruktor bereitstellen können.

In Ihren Beispielen gibt es jedoch keinen Unterschied zwischen ihnen, da selbst der sekundäre Konstruktor ein null als Standardwert übergibt und der primäre Konstruktor auch den Standardwert kennen muss. Ich denke es sollte nicht.

Dies bedeutet, dass es sauberer und besser getrennt wäre, wenn es folgendermaßen implementiert würde:

public DelegateCommand(Action<T> execute) : this(execute, _ => true) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute ?? throw new ArgumentNullException(..);
    this.canExecute = canExecute ?? throw new ArgumentNullException(..);
}

beachte das _ => true an den primären Konstruktor übergeben, der jetzt auch alle Parameter auf null überprüft und sich nicht um Standardeinstellungen kümmert.


Der wichtigste Punkt ist jedoch die Erweiterbarkeit. Mehrere Konstruktoren sind sicherer, wenn die Möglichkeit besteht, dass Sie Ihren Code in Zukunft erweitern. Wenn Sie weitere erforderliche Parameter hinzufügen und die optionalen am Ende stehen müssen, werden alle Ihre aktuellen Implementierungen unterbrochen. Sie können den alten Konstruktor [Obsolete] und informieren Sie die Benutzer, dass es entfernt wird, damit sie Zeit haben, auf die neue Implementierung zu migrieren, ohne ihren Code sofort zu beschädigen.


Andererseits wäre es auch verwirrend, zu viele Parameter optional zu machen, denn wenn einige davon in einem Szenario erforderlich und in einem anderen optional sind, müssten Sie die Dokumentation studieren, anstatt nur den richtigen Konstruktor auszuwählen, indem Sie einfach seine Parameter betrachten.

24
t3chb0t

Wenn man bedenkt, dass das einzige, was Sie im Konstruktor tun, eine einfache Zuweisung ist, ist die Einzelkonstruktorlösung in Ihrem Fall möglicherweise die bessere Wahl. Der andere Konstruktor bietet keine zusätzliche Funktionalität und aus dem Entwurf des Konstruktors mit zwei Parametern geht hervor, dass das zweite Argument nicht angegeben werden muss.

Mehrere Konstruktoren sind sinnvoll, wenn Sie ein Objekt aus verschiedenen Typen erstellen. In diesem Fall ersetzen die Konstruktoren eine externe Factory, da sie die Eingabeparameter verarbeiten und in eine korrekte interne Eigenschaftsdarstellung der Klasse formatieren, die erstellt wird. Dies ist in Ihrem Fall jedoch nicht der Fall, weshalb ein einzelner Konstruktor mehr als genug sein sollte.

17
Andy

Ich denke, es gibt zwei verschiedene Fälle, für die jeder Ansatz glänzen kann.

Erstens, wenn wir einfache Konstruktoren haben (was meiner Erfahrung nach normalerweise der Fall ist), würde ich optionale Argumente in Betracht ziehen, um zu glänzen.

  1. Sie minimieren den Code, der geschrieben werden muss (und damit auch gelesen werden muss).
  2. Sie können sicherstellen, dass sich die Dokumentation an einem Ort befindet und nicht wiederholt wird (was schlecht ist, da dadurch ein zusätzlicher Bereich geöffnet wird, der veraltet sein kann).
  3. Wenn Sie viele optionale Argumente haben, können Sie sehr verwirrende Kombinationen von Konstruktoren vermeiden. Selbst mit nur 2 optionalen Argumenten (die nichts miteinander zu tun haben) müssten Sie, wenn Sie separate, überladene Konstruktoren wünschen, 4 Konstruktoren haben (Version ohne, Version mit jedem und Version mit beiden). Dies lässt sich offensichtlich nicht gut skalieren.

Buuuut, es gibt Fälle, in denen optionale Argumente in Konstruktoren die Dinge nur deutlich verwirrender machen. Das offensichtliche Beispiel ist, wenn diese optionalen Argumente exklusiv sind (dh nicht zusammen verwendet werden können). Überladene Konstruktoren, die nur die tatsächlich gültigen Kombinationen von Argumenten zulassen, würden die Durchsetzung dieser Einschränkung durch die Kompilierungszeit sicherstellen. Sie sollten jedoch auch vermeiden, dass dieser Fall auftritt (z. B. wenn mehrere Klassen von einer Basis erben, bei der jede Klasse das ausschließliche Verhalten ausführt).

3
Kat