I have two classes (A and B) that are both complex to construct, with multiple properties that must be validated at construction time. I want to use the Builder pattern to construct these objects, but among the constraints on construction of these objects are:
- An instance of A must contain multiple instances of B
- Each instance of B has a child relationship to a single instance of A
In addition, I need to be able to add new instances of B to A after A has been constructed.
It seems like this must be a pretty common scenario. Is there some pattern other than Builder or to augment Builder that handles this situation?
Option 1 - With a "placeholder" class
The best solution that I have come up with so far uses an impotent version of the Builder class, with the Build()
method removed (I call it a Placeholder) to specify the parameters for the child class.
class ClassA {
get propertyC
...
get list<A> children
restricted constructor() {}
BBuilder AddChild() {}
}
class ClassB {
get propertyD
...
get ClassA parent
restricted constructor(ClassA parent)
}
class ABuilder {
get / set propertyC
...
BPlaceholder AddChild() {}
ClassA Build() {
VALIDATE properties
BUILD ClassA
foreach child { BUILD child }
}
}
class BPlaceholder {
get / set propertyD
...
restricted constructor(ABuilder parent) {}
}
class BBuilder : BPlaceholder {
restricted constructor(ClassA parent) {}
ClassB Build() {
VALIDATE properties
BUILD child
}
}
Notice that, of the 5 classes in this example, the only one that can be simply constructed is ABuilder
. The other classes are all instantiated, either directly or indirectly, through this class.
Option 2 - Similar to Abstract Factory
Another possibility I have considered is to remove the Build()
method from both Builder classes (in which case they are no longer Builders) and pass both classes to a third Factory class to perform the construction, similar to Abstract Factory. This doesn't seem as nice to me for two reasons, though:
- The interface doesn't intuitively guide you to the proper use
- Construction of B after A has already been instantiated is substantially different from construction of B before A has been instantiated
Rationale for avoidance of simple construction
One possibility is to just use simple construction, something like this:
objA = new ClassA()
objA.Add(new ClassB(objA))
This is obviously a possibility, but it is missing a key element found in the Builder pattern. Builder allows you to ensure that there is no way to construct an "incomplete" instance of a class. My constraints state that a valid instance of ClassA
must have at least one child of type ClassB
. In the example above, objA
exists (for a short time) without any children, and is hence invalid.