Dies wird durch diese Antwort auf eine separate Frage motiviert .
Das Builder-Muster wird verwendet, um die komplexe Initialisierung zu vereinfachen, insbesondere mit optionalen Initialisierungsparametern. Ich weiß jedoch nicht, wie ich mich gegenseitig ausschließende Konfigurationen richtig verwalten soll.
Hier ist eine Image
Klasse. Image
kann aus einer Datei oder aus einer Größe initialisiert werden, aber nicht aus beiden . Die Verwendung von Konstruktoren zur Durchsetzung dieses gegenseitigen Ausschlusses ist offensichtlich, wenn die Klasse einfach genug ist:
public class Image
{
public Image(Size size, Thing stuff, int range)
{
// ... initialize empty with size
}
public Image(string filename, Thing stuff, int range)
{
// ... initialize from file
}
}
Angenommen, es Image
ist tatsächlich so konfigurierbar, dass das Builder-Muster nützlich ist. Dies könnte plötzlich möglich sein:
Image image = new ImageBuilder()
.setStuff(stuff)
.setRange(range)
.setSize(size) // <---------- NOT
.setFilename(filename) // <---------- COMPATIBLE
.build();
Diese Probleme müssen zur Laufzeit und nicht zur Kompilierungszeit behoben werden, was nicht das Schlimmste ist. Das Problem ist, dass das konsequente und umfassende Erkennen dieser Probleme innerhalb der ImageBuilder
Klasse komplex werden kann, insbesondere im Hinblick auf die Wartung.
Wie soll ich mit inkompatiblen Konfigurationen im Builder-Muster umgehen?
Range
undStuff
zuerst initialisiert werden, nicht zu beliebigen Zeiten.RangeAndStuffBuilder
) für den tatsächlichen Typ aufgerufen werden können. Weitere Einschränkungen können implementiert werden, indem für einige Methoden mehr Basaltypen zurückgegeben werden (obwohl dies zu einer exponentiellen Zunahme der Typen führt), wodurch Operationen effektiv entfernt werden. Solange die Methodenergebnisse nicht in der Hierarchie zurückgehen, werden keine Tippfehler angezeigt. DassetHeight
/setWidth
-Szenario könnte mit einer Geschwisterhierarchie implementiert werden, die keinebuild
Methode hat.