Subclass of the Java Builder Class

Give this article to Dr. Dobbs and, in particular, the Builder pattern, how do we handle the case of subclassing Builder? Accepting an abridged version of the example in which we want to subclass to add GMO marking, a naive implementation would be as follows:

public class NutritionFacts { private final int calories; public static class Builder { private int calories = 0; public Builder() {} public Builder calories(int val) { calories = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } protected NutritionFacts(Builder builder) { calories = builder.calories; } } 

Subclass:

 public class GMOFacts extends NutritionFacts { private final boolean hasGMO; public static class Builder extends NutritionFacts.Builder { private boolean hasGMO = false; public Builder() {} public Builder GMO(boolean val) { hasGMO = val; return this; } public GMOFacts build() { return new GMOFacts(this); } } protected GMOFacts(Builder builder) { super(builder); hasGMO = builder.hasGMO; } } 

Now we can write this code:

 GMOFacts.Builder b = new GMOFacts.Builder(); b.GMO(true).calories(100); 

But, if we get the wrong order, all of this fails:

 GMOFacts.Builder b = new GMOFacts.Builder(); b.calories(100).GMO(true); 

The problem, of course, is that NutritionFacts.Builder returns NutritionFacts.Builder and not GMOFacts.Builder , since we are solving this problem or is there a better template to use?

Note: this answer to a similar question suggests the classes that I have above; my question is about ensuring that the builder's challenges are in the correct order.

+120
java design-patterns
Jun 18 '13 at 8:44
source share
8 answers

You can solve it with generics. I think this is called "Curiously Repeating Common Templates"

Make the return type of methods of the base class the construction of a common argument.

 public class NutritionFacts { private final int calories; public static class Builder<T extends Builder<T>> { private int calories = 0; public Builder() {} public T calories(int val) { calories = val; return (T) this; } public NutritionFacts build() { return new NutritionFacts(this); } } protected NutritionFacts(Builder<?> builder) { calories = builder.calories; } } 

Now create a base builder with building a derived class as a general argument.

 public class GMOFacts extends NutritionFacts { private final boolean hasGMO; public static class Builder extends NutritionFacts.Builder<Builder> { private boolean hasGMO = false; public Builder() {} public Builder GMO(boolean val) { hasGMO = val; return this; } public GMOFacts build() { return new GMOFacts(this); } } protected GMOFacts(Builder builder) { super(builder); hasGMO = builder.hasGMO; } } 
+155
Jun 18 '13 at 9:19
source share

For recording only, to get rid of

unchecked or unsafe operations warning

for the operator return (T) this; as @dimadima and @Thomas N. say, in some cases the following solution applies.

Make an abstract builder that declares a generic type ( T extends Builder in this case) and declare a protected abstract T getThis() abstract method as follows:

 public abstract static class Builder<T extends Builder<T>> { private int calories = 0; public Builder() {} /** The solution for the unchecked cast warning. */ public abstract T getThis(); public T calories(int val) { calories = val; // no cast needed return getThis(); } public NutritionFacts build() { return new NutritionFacts(this); } } 

See http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 for details.

+34
Jan 12 '16 at 10:59
source share

Based on a blog post , this approach requires all non-classical classes to be abstract, and all sheet classes must be final.

 public abstract class TopLevel { protected int foo; protected TopLevel() { } protected static abstract class Builder <T extends TopLevel, B extends Builder<T, B>> { protected T object; protected B thisObject; protected abstract T createObject(); protected abstract B thisObject(); public Builder() { object = createObject(); thisObject = thisObject(); } public B foo(int foo) { object.foo = foo; return thisObject; } public T build() { return object; } } } 

Then you have an intermediate class that extends this class and its builder and as much as you need:

 public abstract class SecondLevel extends TopLevel { protected int bar; protected static abstract class Builder <T extends SecondLevel, B extends Builder<T, B>> extends TopLevel.Builder<T, B> { public B bar(int bar) { object.bar = bar; return thisObject; } } } 

And finally, a specific leaf class that can call all the construction methods for any of its parents in any order:

 public final class LeafClass extends SecondLevel { private int baz; public static final class Builder extends SecondLevel.Builder<LeafClass,Builder> { protected LeafClass createObject() { return new LeafClass(); } protected Builder thisObject() { return this; } public Builder baz(int baz) { object.baz = baz; return thisObject; } } } 

You can then call the methods in any order from any of the classes in the hierarchy:

 public class Demo { LeafClass leaf = new LeafClass.Builder().baz(2).foo(1).bar(3).build(); } 
+19
May 22 '14 at 20:07
source share

You can also override the calories() method and let it return an expanding builder. This compiles because Java supports covariant return types .

 public class GMOFacts extends NutritionFacts { private final boolean hasGMO; public static class Builder extends NutritionFacts.Builder { private boolean hasGMO = false; public Builder() { } public Builder GMO(boolean val) { hasGMO = val; return this; } public Builder calories(int val) { super.calories(val); return this; } public GMOFacts build() { return new GMOFacts(this); } } [...] } 
+6
Jun 18 '13 at 9:07 on
source share

If you don’t want to show your eye on an angular bracket or three, or maybe you don’t feel ... mmm ... I mean ... cough . the rest of your team quickly comprehends a curiously repeating generic pattern; you can do this:

 public class TestInheritanceBuilder { public static void main(String[] args) { SubType.Builder builder = new SubType.Builder(); builder.withFoo("FOO").withBar("BAR").withBaz("BAZ"); SubType st = builder.build(); System.out.println(st.toString()); builder.withFoo("BOOM!").withBar("not getting here").withBaz("or here"); } } 

supported by

 public class SubType extends ParentType { String baz; protected SubType() {} public static class Builder extends ParentType.Builder { private SubType object = new SubType(); public Builder withBaz(String baz) { getObject().baz = baz; return this; } public Builder withBar(String bar) { super.withBar(bar); return this; } public Builder withFoo(String foo) { super.withFoo(foo); return this; } public SubType build() { // or clone or copy constructor if you want to stamp out multiple instances... SubType tmp = getObject(); setObject(new SubType()); return tmp; } protected SubType getObject() { return object; } private void setObject(SubType object) { this.object = object; } } public String toString() { return "SubType2{" + "baz='" + baz + '\'' + "} " + super.toString(); } } 

and parent type:

 public class ParentType { String foo; String bar; protected ParentType() {} public static class Builder { private ParentType object = new ParentType(); public ParentType object() { return getObject(); } public Builder withFoo(String foo) { if (!"foo".equalsIgnoreCase(foo)) throw new IllegalArgumentException(); getObject().foo = foo; return this; } public Builder withBar(String bar) { getObject().bar = bar; return this; } protected ParentType getObject() { return object; } private void setObject(ParentType object) { this.object = object; } public ParentType build() { // or clone or copy constructor if you want to stamp out multiple instances... ParentType tmp = getObject(); setObject(new ParentType()); return tmp; } } public String toString() { return "ParentType2{" + "foo='" + foo + '\'' + ", bar='" + bar + '\'' + '}'; } } 

Key points:

  • Encapsulate an object in the builder so that inheritance does not allow you to set a field on an object stored in the parent type
  • Super calls ensure that the logic (if any) is added to the super-type construction methods, stored in subtypes.
  • The down side is the creation of false objects in the parent class (s) ... But see below to clear this.
  • Upstairs is much easier to understand at a glance, and there are no detailed constructors that convey properties.
  • If you have several threads accessing your building objects ... I guess I'm glad that I am not for you :).

EDIT:

I found a way to create false objects. First add this to each builder:

 private Class whoAmI() { return new Object(){}.getClass().getEnclosingMethod().getDeclaringClass(); } 

Then in the constructor for each builder:

  if (whoAmI() == this.getClass()) { this.obj = new ObjectToBuild(); } 

Cost is an additional class file for the anonymous inner class new Object(){}

+2
Mar 17 '16 at 3:27
source share

There is also another way to create classes according to the Builder pattern, which corresponds to “Prefers composition over inheritance”.

Define the interface that the parent class Builder inherits:

 public interface FactsBuilder<T> { public T calories(int val); } 

The implementation of NutritionFacts almost the same (with the exception of the Builder implementation of the FactsBuilder interface):

 public class NutritionFacts { private final int calories; public static class Builder implements FactsBuilder<Builder> { private int calories = 0; public Builder() { } @Override public Builder calories(int val) { return this; } public NutritionFacts build() { return new NutritionFacts(this); } } protected NutritionFacts(Builder builder) { calories = builder.calories; } } 

Builder child class must extend the same interface (except for another common implementation):

 public static class Builder implements FactsBuilder<Builder> { NutritionFacts.Builder baseBuilder; private boolean hasGMO = false; public Builder() { baseBuilder = new NutritionFacts.Builder(); } public Builder GMO(boolean val) { hasGMO = val; return this; } public GMOFacts build() { return new GMOFacts(this); } @Override public Builder calories(int val) { baseBuilder.calories(val); return this; } } 

Note that NutritionFacts.Builder is a field inside GMOFacts.Builder (called baseBuilder ). The method implemented from the FactsBuilder interface calls the baseBuilder method with the same name:

 @Override public Builder calories(int val) { baseBuilder.calories(val); return this; } 

There is also a big change in GMOFacts(Builder builder) . The first constructor call to the parent class constructor should pass the appropriate NutritionFacts.Builder :

 protected GMOFacts(Builder builder) { super(builder.baseBuilder); hasGMO = builder.hasGMO; } 

Full implementation of the GMOFacts class:

 public class GMOFacts extends NutritionFacts { private final boolean hasGMO; public static class Builder implements FactsBuilder<Builder> { NutritionFacts.Builder baseBuilder; private boolean hasGMO = false; public Builder() { } public Builder GMO(boolean val) { hasGMO = val; return this; } public GMOFacts build() { return new GMOFacts(this); } @Override public Builder calories(int val) { baseBuilder.calories(val); return this; } } protected GMOFacts(Builder builder) { super(builder.baseBuilder); hasGMO = builder.hasGMO; } } 
+2
Oct 17 '16 at 13:10
source share

One thing you can do is create a static factory method in each of your classes:

 NutritionFacts.newBuilder() GMOFacts.newBuilder() 

This static factory method will then return the appropriate builder. You can have the GMOFacts.Builder extension of NutritionFacts.Builder , which is not a problem. The problem here will be with visibility ...

+1
Jun 18 '13 at 8:57
source share

I created a parent abstract abstract builder class that takes two parameters of a formal type. First, for the type of object returned by the build () function, the second type returned by each optional parameterizer. The following are the parent and child classes to illustrate:

 **Parent** public abstract static class Builder<T, U extends Builder<T, U>> { // Required parameters private final String name; // Optional parameters private List<String> outputFields = null; public Builder(String pName) { name = pName; } public U outputFields(List<String> pOutFlds) { outputFields = new ArrayList<>(pOutFlds); return getThis(); } /** * This helps avoid "unchecked warning", which would forces to cast to "T" in each of the optional * parameter setters.. * @return */ abstract U getThis(); public abstract T build(); /* * Getters */ public String getName() { return name; } } **Child** public static class Builder extends AbstractRule.Builder<ContextAugmentingRule, ContextAugmentingRule.Builder> { // Required parameters private final Map<String, Object> nameValuePairsToAdd; // Optional parameters private String fooBar; Builder(String pName, Map<String, String> pNameValPairs) { super(pName); /** * Must do this, in case client code (Ie JavaScript) is re-using * the passed in for multiple purposes. Doing {@link Collections#unmodifiableMap(Map)} * won't caught it, because the backing Map passed by client prior to wrapping in * unmodifiable Map can still be modified. */ nameValuePairsToAdd = new HashMap<>(pNameValPairs); } public Builder fooBar(String pStr) { fooBar = pStr; return this; } @Override public ContextAugmentingRule build() { try { Rule r = new ContextAugmentingRule(this); storeInRuleByNameCache(r); return (ContextAugmentingRule) r; } catch (RuleException e) { throw new IllegalArgumentException(e); } } @Override Builder getThis() { return this; } } 

It satisfied my satisfaction needs.

0
Nov 15 '17 at 22:39
source share



All Articles