Subclassing a Java Builder class
Subclassing a Java Builder class
Give this Dr Dobbs article, and the Builder Pattern in particular, how do we handle the case of subclassing a Builder? Taking a cut-down version of the example where we want to subclass to add GMO labelling, a naive implementation would be:
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 code like this:
GMOFacts.Builder b = new GMOFacts.Builder(); b.GMO(true).calories(100);
But, if we get the order wrong, it all fails:
GMOFacts.Builder b = new GMOFacts.Builder(); b.calories(100).GMO(true);
The problem is of course that NutritionFacts.Builder
returns a NutritionFacts.Builder
, not a GMOFacts.Builder
, so how do we solve this problem, or is there a better Pattern to use?
Note: this answer to a similar question offers up the classes I have above; my question is regarding the problem of ensuring the builder calls are in the correct order.
Answer by fge for Subclassing a Java Builder class
One thing you could do is to create a static factory method in each of your classes:
NutritionFacts.newBuilder() GMOFacts.newBuilder()
This static factory method would then return the appropriate builder. You can have a GMOFacts.Builder
extending a NutritionFacts.Builder
, that is not a problem. THE problem here will be to deal with visibility...
Answer by Flavio for Subclassing a Java Builder class
You can override also the calories()
method, and let it return the extending 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); } } [...] }
Answer by gkamal for Subclassing a Java Builder class
You can solve it using generics. I think this is called the "Curiously recurring generic patterns"
Make the return type of the base class builder methods a generic argument.
public class NutritionFacts { private final int calories; public static class Builder { 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 instantiate the base builder with the derived class builder as the generic argument.
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; } }
Answer by Q23 for Subclassing a Java Builder class
Slightly more complicated, but here's a solution to your problem, based off of this blog post I found once when I was looking to do exactly this. This method requires all the non-leaf classes to be abstract, and all the leaf classes to be final, or Bad Things could happen.
public abstract class TopLevel { protected int foo; protected TopLevel() { } protected static abstract class Builder> { 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 some intermediate class that extends this class and its builder, and as many more as you need:
public abstract class SecondLevel extends TopLevel { protected int bar; protected static abstract class Builder> extends TopLevel.Builder { public B bar(int bar) { object.bar = bar; return thisObject; } } }
And, finally a concrete leaf class that can call all the builder methods on any of its parents in any order:
public final class LeafClass extends SecondLevel { private int baz; public static final class Builder extends SecondLevel.Builder { protected LeafClass createObject() { return new LeafClass(); } protected Builder thisObject() { return this; } public Builder baz(int baz) { object.baz = baz; return thisObject; } } }
Then, you can 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(); }
Answer by Stepan Vavra for Subclassing a Java Builder class
Just for the record, to get rid of the
unchecked or unsafe operations
warning
for the return (T) this;
statement as @dimadima and @Thomas N. talk about, following solution applies in certain cases.
Make abstract
the builder which declares the generic type (T extends Builder
in this case) and declare protected abstract T getThis()
abstract method as follows:
public abstract static class Builder> { 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); } }
Refer to http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 for further details.
Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72
0 comments:
Post a Comment