There a few options the way I see it:
Option 1: Pass the Builder Around
There's no benefit in actually building and returning an object that will need to changed. Just pass the Builder
object where ever it needs to go and provide access to its fields so that whatever needs to happen between each build stage can know the current state of the builder. The downsides to this are that you cant "lock in" a part of the Builders
state if that is something you need to do. It can technically be done by having a function lockInStage1()
or something which changes a flag that stops some setters from being used, but thats a bit messy.
Option 2: Provide a ReadOnly implementation
This is basically the same thing as Option 1, but without the Builder
object. Just have a mutable version of the object and then allow the user to create a immutable version from it.
For example:
public class ReadOnlyMyObject {
private int field1;
private int field2;
public ReadOnlyMyObject(MyObject obj) {
field1 = obj.field1;
field2 = obj.field2;
}
public int getField1() {
return field1;
}
}
public class MyObject {
public int field1;
public int field2;
//fields can either be public or accessed through getters and setters
public ReadOnlyMyObject asReadOnly() {
return new ReadOnlyMyObject(this);
}
}
Personally, I prefer this over the builder pattern as it leaves you open to having a mutable object if it is ever needed, and why have a Builder
class if the object isn't being built as one piece. It has the same disadvantage as Option 1 though. You can't really lock in the state of each stage.
Option 3: Builder for each Stage
Instead of a single object and Builder
counterpart you would simply make an object for each stage and build it individually. The Builder
for each stage would then take as a parameter the previous stage.
public class Stage1 {
//stage 1 fields
public class Builder {
//Builder fields
public Builder() { }
//stage 1 builder functions
public Stage1 build() {
//build stage 1
}
}
}
public class Stage2 {
private Stage1 stage1
//stage 2 fields
public class Builder {
private Stage1 stage1;
//Builder fields
public Builder(Stage1 stage1) {
this.stage1 = stage1;
}
//stage 2 builder functions
public Stage1 build() {
Stage2 stage2 = new Stage2(this.stage1);
//build stage 2
}
}
}
public class FinalObject {
private Stage2 stage2
//fields
public class Builder {
private Stage2 stage2;
//Builder fields
public Builder(Stage2 stage2) {
this.stage2 = stage2;
}
//builder functions
public FinalObject build() {
FinalObject obj = new FinalObject(this.stage2);
//build final object
}
}
}
I don't love this, because you can potentially need to create a lot of classes. However there are some real benefits to this method.
Each stage is locked in. Once it is created it can't be changed.
If you need to add stages between 2 existing stages its fairly simple (kind of like how adding an object to a LinkedList
has very little overhead. For that reason the stage objects shouldn't be called Stage
, Stage2
or any ordinal value as it would force you to rename all of them.
Summary
Overall I would say if the stages need to be locked in go with the Multiple Builders as long as you don't need to change any data in a previous stage once it is created. It can be done, but its fighting the pattern.
If the data in each stage can potentially be changed by what happens in later stages go with passing the Builder
or providing a ReadOnly version of the object.