Skip to main content

How to improve the builder pattern?


Motivation



Recently I searched for a way to initialize a complex object without passing a lot of parameter to the constructor. I tried it with the builder pattern, but I don't like the fact, that I'm not able to check at compile time if I really set all needed values.



Traditional builder pattern



When I use the builder pattern to create my Complex object, the creation is more "typesafe", because it's easier to see what an argument is used for:




new ComplexBuilder()
.setFirst( "first" )
.setSecond( "second" )
.setThird( "third" )
...
.build();



But now I have the problem, that I can easily miss an important parameter. I can check for it inside the build() method, but that is only at runtime. At compile time there is nothing that warns me, if I missed something.



Enhanced builder pattern



Now my idea was to create a builder, that "reminds" me if I missed a needed parameter. My first try looks like this:



public class Complex {
private String m_first;
private String m_second;
private String m_third;

private Complex() {}

public static class ComplexBuilder {
private Complex m_complex;

public ComplexBuilder() {
m_complex = new Complex();
}

public Builder2 setFirst( String first ) {
m_complex.m_first = first;
return new Builder2();
}

public class Builder2 {
private Builder2() {}
Builder3 setSecond( String second ) {
m_complex.m_second = second;
return new Builder3();
}
}

public class Builder3 {
private Builder3() {}
Builder4 setThird( String third ) {
m_complex.m_third = third;
return new Builder4();
}
}

public class Builder4 {
private Builder4() {}
Complex build() {
return m_complex;
}
}
}
}


As you can see, each setter of the builder class returns a different internal builder class. Each internal builder class provides exactly one setter method and the last one provides only a build() method.



Now the construction of an object again looks like this:




new ComplexBuilder()
.setFirst( "first" )
.setSecond( "second" )
.setThird( "third" )
.build();



...but there is no way to forget a needed parameter. The compiler wouldn't accept it.



Optional parameters



If I had optional parameters, I would use the last internal builder class Builder4 to set them like a "traditional" builder does, returning itself.



Questions



  • Is this a well known pattern? Does it have a special name?

  • Do you see any pitfalls?

  • Do you have any ideas to improve the implementation - in the sense of fewer lines of code?


Source: Tips4allCCNA FINAL EXAM

Comments

  1. No, it's not new. What you're actually doing there is creating a sort of a DSL by extending the standard builder pattern to support branches which is among other things an excellent way to make sure the builder doesn't produce a set of conflicting settings to the actual object.

    Personally I think this is a great extension to builder pattern and you can do all sorts of interesting things with it, for example at work we have DSL builders for some of our data integrity tests which allow us to do things like assertMachine().usesElectricity().and().makesGrindingNoises().whenTurnedOn();. OK, maybe not the best possible example but I think you get the point.

    ReplyDelete
  2. The traditional builder pattern already handles this: simply take the mandatory parameters in the constructor. Of course, nothing prevents a caller from passing null, but neither does your method.

    The big problem I see with your method is that you either have a combinatorical explosion of classes with the number of mandatory parameters, or force the user to set the parameters in one particular sqeuence, which is annoying.

    Also, it is a lot of additional work.

    ReplyDelete
  3. Why don't you put "needed" parameters in the builders constructor?

    public class Complex
    {
    ....
    public static class ComplexBuilder
    {
    // Required parameters
    private final int required;

    // Optional parameters
    private int optional = 0;

    public ComplexBuilder( int required )
    {
    this.required = required;
    }

    public Builder setOptional(int optional)
    {
    this.optional = optional;
    }
    }
    ...
    }


    This pattern is outlined in Effective Java.

    ReplyDelete
  4. IMHO, this seems bloated. If you have to have all the parameters, pass them in the constructor.

    ReplyDelete
  5. I've seen/used this:

    new ComplexBuilder(requiredvarA, requiedVarB).optional(foo).optional(bar).build();

    Then pass these to your object that requires them.

    ReplyDelete
  6. For more information on when to use the Builder Pattern and its advantages you should check out my post for another similar question here

    ReplyDelete
  7. Instead of using multiple classes I would just use one class and multiple interfaces. It enforces your syntax without requiring as typing. It also allows you to see all related code close together which makes it easier to understand what is going on with your code at a larger level.

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex