In Java und C # können Sie ein Objekt mit Eigenschaften erstellen, die bei der Initialisierung festgelegt werden können, indem Sie entweder einen Konstruktor mit Parametern definieren, jede Eigenschaft nach dem Erstellen des Objekts definieren oder das Schnittstellenmuster Builder / Fluid verwenden. In C # 3 wurden jedoch Objekt- und Sammlungsinitialisierer eingeführt, was bedeutete, dass das Builder-Muster weitgehend nutzlos war. In einer Sprache ohne Initialisierer könnte man einen Builder implementieren und ihn dann folgendermaßen verwenden:
Vehicle v = new Vehicle.Builder()
.manufacturer("Toyota")
.model("Camry")
.year(1997)
.colour(CarColours.Red)
.addSpecialFeature(new Feature.CDPlayer())
.addSpecialFeature(new Feature.SeatWarmer(4))
.build();
Umgekehrt könnte man in C # schreiben:
var vehicle = new Vehicle {
Manufacturer = "Toyota",
Model = "Camry",
Year = 1997,
Colour = CarColours.Red,
SpecialFeatures = new List<SpecialFeature> {
new Feature.CDPlayer(),
new Feature.SeatWarmer { Seats = 4 }
}
}
... ohne Builder, wie im vorherigen Beispiel gezeigt.
Sind Builder nach diesen Beispielen in C # noch nützlich oder wurden sie vollständig von Initialisierern abgelöst?
quelle
are builders useful
Warum sehe ich diese Art von Fragen immer wieder? Ein Builder ist ein Entwurfsmuster - sicher, es kann als Sprachmerkmal verfügbar gemacht werden, aber am Ende des Tages ist es nur ein Entwurfsmuster. Ein Entwurfsmuster kann nicht "falsch" sein. Es kann Ihren Anwendungsfall erfüllen oder nicht, aber das macht nicht das gesamte Muster falsch, sondern nur die Anwendung. Denken Sie daran, dass Designmuster am Ende des Tages bestimmte Probleme lösen. Wenn Sie sich dem Problem nicht stellen, warum sollten Sie dann versuchen, es zu lösen?Antworten:
Wie von @ user248215 angesprochen, ist das eigentliche Problem die Unveränderlichkeit. Der Grund, warum Sie einen Builder in C # verwenden würden, besteht darin, die Aussagekraft eines Initialisierers beizubehalten, ohne einstellbare Eigenschaften verfügbar machen zu müssen. Es ist keine Frage der Kapselung, weshalb ich meine eigene Antwort geschrieben habe. Die Kapselung ist ziemlich orthogonal, da das Aufrufen eines Setters nicht impliziert, was der Setter tatsächlich tut, oder Sie an seine Implementierung bindet.
In der nächsten Version von C #, 8.0, wird wahrscheinlich ein
with
Schlüsselwort eingeführt, mit dem unveränderliche Objekte klar und präzise initialisiert werden können, ohne dass Builder geschrieben werden müssen.Eine andere interessante Sache, die Sie mit Buildern im Gegensatz zu Initialisierern tun können, ist, dass sie abhängig von der Reihenfolge der aufgerufenen Methoden zu unterschiedlichen Objekttypen führen können.
Beispielsweise
Um das obige Beispiel zu verdeutlichen, handelt es sich um eine Mustervergleichsbibliothek, die das Builder-Muster verwendet, um eine "Übereinstimmung" durch Angabe von Fällen aufzubauen. Fälle werden angehängt, indem die
Case
Methode aufgerufen wird, die eine Funktion übergibt. Wennvalue
es dem Parametertyp der Funktion zugewiesen werden kann, wird es aufgerufen. Sie finden den vollständigen Quellcode auf GitHub . Da XML-Kommentare im Klartext schwer zu lesen sind, finden Sie hier einen Link zur von SandCastle erstellten Dokumentation (siehe Abschnitt " Hinweise ").quelle
IEnumerable<Func<T, TResult>>
Mitglieds nicht möglich war.TResult
. Letztendlich hatte Typinferenz viel damit zu tun. Ich wollte auch, dass es wie eine Kontrollstruktur "aussieht". Außerdem wollte ich keinen Initialisierer verwenden, da dies eine Mutation ermöglichen würde.Objektinitialisierer erfordern, dass der aufrufende Code auf die Eigenschaften zugreifen kann. Verschachtelte Builder können auf private Mitglieder der Klasse zugreifen.
Wenn Sie
Vehicle
unveränderlich machen möchten (indem Sie alle Setter privat machen), können Sie die privaten Variablen mit dem verschachtelten Builder festlegen.quelle
Sie alle dienen unterschiedlichen Zwecken !!!
Konstruktoren können markierte Felder
readonly
sowie private und geschützte Mitglieder initialisieren . Sie sind jedoch etwas eingeschränkt in dem, was Sie in einem Konstruktor tun können. Sie sollten beispielsweise vermeiden,this
an externe Methoden zu übergeben, und Sie sollten vermeiden, virtuelle Mitglieder aufzurufen, da diese möglicherweise im Kontext einer abgeleiteten Klasse ausgeführt werden, die sich noch nicht selbst erstellt hat. Außerdem wird die Ausführung von Konstruktoren garantiert (es sei denn, der Aufrufer führt etwas sehr Ungewöhnliches aus). Wenn Sie also Felder im Konstruktor festlegen, kann der Code in der restlichen Klasse davon ausgehen, dass diese Felder nicht null sind.Initialisierer laufen nach dem Bau. Sie können nur öffentliche Objekte aufrufen. Private und / oder schreibgeschützte Felder können nicht festgelegt werden. Konventionell sollte das Festlegen einer Eigenschaft ziemlich begrenzt sein, z. B. sollte es idempotent sein und begrenzte Nebenwirkungen haben.
Builder- Methoden sind tatsächliche Methoden und erlauben daher mehr als ein Argument. Sie können konventionell Nebenwirkungen haben, einschließlich der Objekterstellung. Während sie keine schreibgeschützten Felder festlegen können, können sie so ziemlich alles andere tun. Methoden können auch als Erweiterungsmethoden implementiert werden (wie so ziemlich die gesamte LINQ-Funktionalität).
quelle