I have created a builder for client types A and B.
Client TypeA has two fields fieldA and fieldB (see below), while client TypeB has two additional fields. How can I make sure field C and D are only accessible to client TypeB, i.e. client TypeA should not take field fieldC and fieldD when creating it.
Construction client A:
client.builder()
.withClientType(ClientType.TypeA)
.withFieldA(fieldA)
.withFieldB(fieldB)
.build();
Construction client B:
client.builder()
.withClientType(ClientType.TypeB)
.withFieldA(fieldA)
.withFieldB(fieldB)
.withFieldC(fieldC)
.withFieldD(fieldD)
.build();
What's the correct usage builder pattern in this case?
It's not possible to construct instances of different classes using the same Builder. As @RDK has pointed out in the comments you have to implement the Builder pattern for each of your classes.
Builder might have the same hierarchy as the classes they are meant to instantiate. That would allow to reuse the functionality of the parent-builder.
Below, I've provided a hierarchy of Builders inspired by the classic implementation from the "Effective Java" by Joshua Bloch (a former Sun Microsystems employee, who led the design and implementation of many Java platform features, including the Java Collections Framework).
Firstly, it's worth noticing that using Builder with inheritance adds a bit of complexity because methods all methods of the Builder should be self-returning, i.e. in order to chain methods each method except
build()returnsthisinstance of Builder. But it should a concrete Builder, not it's parent type (because parent might be unaware of all the methods that child has declared).The answer to this problem would be a self-referencing generic type, in the "Effective Java" a generic type with a recursive type parameter. Don't get intimidated, pause for a second and try to recall how
java.lang.Enumlooks like.Enum<E extends Enum<E>>- that's an example of the self-reference, recursively pointing at a particular subclass ofjava.lang.Enum.Similarly, we need a generic builder referencing to itself
ClientBuilder<T extends ClientBuilder<T>>and declaring a self-returning abstract methodself()(which return an instance ofT) that would be implemented by every concrete class.Note: there's no need in enum
ClientTypeto differentiate betweenClientAandClientB, since they are already different classes.That how implementation of an
AbstractClientand two concrete client, each having static nested Builder might look like.Abstract classes
AbstractClient+ClientBuilderConcrete classes
ClientA+ABuilderConcrete classes
ClientB+BBuilderUsage example (dummy classes A, B, C, D are not exposed, use the link below to see the code in action):
Output:
A link to Online Demo.