Statische Erstellungsmethode - Vor- und Nachteile im Vergleich zu Konstruktoren

11

Was sind die Vor- und Nachteile statischer Objekterstellungsmethoden gegenüber Konstruktoren?

class Foo {
  private Foo(object arg) { }

  public static Foo Create(object arg) {
    if (!ValidateParam(arg)) { return null; }
    return new Foo(arg);
  }
}

Wenige, an die ich denken kann:

Vorteile:

  • Geben Sie null zurück, anstatt eine Ausnahme auszulösen (benennen Sie es TryCreate). Dies kann den Code auf der Clientseite knapper und sauberer machen. Clients erwarten selten, dass ein Konstruktor ausfällt.
  • Erstellen Sie verschiedene Arten von Objekten mit klarer Semantik, z. B. CreatFromName(String name)undCreateFromCsvLine(String csvLine)
  • Kann bei Bedarf ein zwischengespeichertes Objekt oder eine abgeleitete Implementierung zurückgeben.

Nachteile:

  • Weniger auffindbar, schwieriger zu überfliegen.
  • Einige Muster, wie die Serialisierung oder Reflexion sind schwieriger ( zum Beispiel Activator<Foo>.CreateInstance())
dbkk
quelle
1
Es ist langsamer. Sie müssen sowieso mit Nullreferenzen umgehen.
Amir Rezaei
1
@Amir Handling null ist ( Foo x = Foo.TryCreate(); if (x == null) { ... }). Die Behandlung einer ctor-Ausnahme ist ( Foo x; try { x = new Foo(); } catch (SomeException e) { ... }). Beim Aufrufen einer normalen Methode bevorzuge ich Ausnahmen gegenüber Fehlercodes, aber bei der Objekterstellung TryCreateerscheint dies sauberer.
dbkk
Würden Sie bei Ihrer Validierung eine Typprüfung durchführen?
Amir Rezaei
In Bezug auf das zweite Pro, "Erstellen Sie verschiedene Arten von Objekten mit klarer Semantik, z. B. CreatFromName (String name) und CreateFromCsvLine (String csvLine)", ist es möglicherweise besser, Anforderungen zu erstellen Nameund zu typisieren CsvLine, als Anforderungen über Methodennamen auszudrücken. Auf diese Weise können Sie Create überladen. Die Verwendung von Zeichenfolgen für beide kann als "primitive Besessenheit" angesehen werden (vorausgesetzt, Sie haben diese Auswahl aus bekannten Leistungsgründen nicht getroffen). Schauen Sie sich Object Calisthenics an, um eine unterhaltsame Möglichkeit zu finden, dies zu erkunden.
Bentayloruk

Antworten:

7

Der größte Nachteil bei statischen " Erstellern " ist wahrscheinlich die Einschränkung der Vererbung. Wenn Sie oder der Benutzer Ihrer Bibliothek eine Klasse von Ihrer ableiten Foo, Foo::Create()wird dies ziemlich nutzlos. Alle dort definierten Logiken müssen im geerbten erneut geschrieben werden Create().

Ich würde einen Kompromiss vorschlagen: Definieren Sie einen Konstruktor mit einer trivialen Objektinitialisierungslogik, die niemals fehlschlägt / wirft, und definieren Sie dann Ersteller mit Caching, alternativer Konstruktion usw. Dies lässt die Möglichkeit des Ableitens und Sie profitieren davon, Ersteller für eine bestimmte Klasse zu haben .

Mojuba
quelle
1
In der Praxis ist dies kein wirkliches Problem, da die meisten Klassen nicht für die Vererbung ausgelegt sind (und daher versiegelt / endgültig sein sollten). Wenn sich später herausstellt, dass Sie die Klasse für die Vererbung öffnen möchten, können Sie jede Initialisierungslogik jederzeit in einen geschützten Konstruktor verschieben.
Doval
7

Erstellen ist eine Factory-Methode. Wenn ich dies für nötig halte, implementiere ich das Factory-Muster und definiere eine Schnittstelle zur Darstellung des Vertrags zum Erstellen des Objekts sowie eine implementierende Klasse, die dann bei Bedarf eingefügt wird. Dadurch bleiben die Vorteile des Verschiebens der Erstellungsverantwortung aus der Klasse erhalten, es werden jedoch die Einschränkungen bei der Objekterstellung außerhalb des Klassenkonstruktors vermieden (oder zumindest deutlicher).

Quentin-Starin
quelle