Builder pattern with a twist

The builder pattern is great, isn’t it? It lets you create nice immutable classes without the need for multiple constructors and it gives the API users freedom in choosing which arguments they want to use when creating the instance. But what happens when you want to tell the user that she must call one builder method or the other, since it is crucial for the class you’re trying to build? The builder pattern simply doesn’t have such a feature. This post will try to give an alternative solution to this problem.

But lets take it from the beginning.

Part 1: Why builder pattern?

Suppose you want to create a class name Address. And suppose you want it to have fields like protocol, url, port, path and description. Now, you want to give your users the freedom to choose which arguments to pass when creating an instance of the class. Also, you want to make your class immutable, so set methods are out of the question. You might start up with something like:

…but you start realizing there is a problem here. First, maybe you need more combinations, like creating an instance given an url, a port and a path, but – both url and path are Strings, and you already have a constructor which takes two String and an int, and, hey, you know you can’t have two constructors with the same signature! Secondly, you realize there’s simply too many constructors here, and it reminds you of something you read once in a book about code smells. The book suggests solving this by replacing constructors with creation methods. Using this tip, your class might look like this:

…but this is also tedious. There’s simply too many combinations. And you need to create an endlessly amount of factory methods to allow all of them. It is then that it strikes you! Why didn’t you consider using the builder pattern! This way, you don’t have to create loads of constructors or creation methods, and still allow the user to create an immutable instance of your class while freely choosing which arguments to use. Your beautiful new class now looks like that:

…and you can create a new instance of Address by calling:

Happy? Very. But there’s just one tiny problem. What happens if the user of this class tries to create an Address without an url or a port? You simply cant allow that now, can you? So you start thinking. And you realize you simply cannot find a good solution. So you ask a question in Stackoverflow and hope someone would give you a good solution. Most of the answers you get there don’t help at all, but after a while, someone called pgras gives you this fantastic solution, and, well, you like it so much you decide to write a blog post about it :)

 

Part 2: Mandatory methods in builder pattern

The solution is to change you builder into a state machine. Each mandatory method in it leads you to the next step, until you get to the last step which includes both non-mandatory methods and the build method itself. It uses interfaces to define the different steps, and, well, it look like that:

Using it is simple:

Try using it without the mandatory methods, and you get a compilation error:

Autocomplete is also helpful here, and will only give you the correct options:

 

So there you have it. The API user can now use your friendly API to create a robust and immutable class knowing that she’ll get all the help needed along the way, using autocomplete and compilation errors if she did something she wasn’t suppose to do. Nothing here can go wrong!

Thanks again, Stackoverflow and  pgras for this nice solution.

22 Comments

  1. Amir

    In your example, you can do this:

    Address.builder().build();

    but if you want to avoid it, you should have more than one static class…

  2. Uzi Landsmann

    Hmmm I’m getting an error when trying this, since builder() returns an instance of Url, which only defines method url()…

  3. Kristoffer

    You can do
    new Address.Builder().build();

    Easily fixed by hiding the constructor for the Builder.

    I like this pattern!

  4. Amir

    I forgot to say that I like this pattern!

  5. Christian

    Very elegant!

  6. @Amir: How were you able to achieve “Address.builder().build()”? I can’t do that, because Adress.builder’s return type is Url and thus hides the rest of Builder’s methods.

    Like Kristoffer pointed out, having a private constructor for Builder would further lower chances of Builder getting used incorrectly.

    Thanks Uzi for this useful pattern!

  7. Fredrik Yttergren

    Why not ask for the mandatory arguments in the Builder’s constructor instead? That’s how Josh Bloch does it in Effective Java iirc, and so I always do that too.

  8. Fredrik Yttergren

    I.e. like this:
    public static class Builder {
    //Mandatory
    private final String url;
    private final int port;

    //Optional
    private String protocol; //or set some sensible default
    private String path; //or set some sensible default
    private String description; //or set some sensible default

    public Builder(final String url, final int port){
    this.url=url;
    this.port=port;
    }

    … setters for the optional ones

    … the build() method
    }

  9. Fredrik Yttergren

    Also, what stops me from casting the Url i get from Address.builder() to a Builder and calling build() on it?

  10. Lovely pattern.

    @Fredrik: “what stops me”, the API is not trying to stop you from creating bad objects. It’s trying to help you and it does so wonderfully.

    The problem with your mandatory arguments to the contructor is that the call new Something(“hello”, “name”); does not tell the reader what you’re passing in.

    builder().name(“name”).greetWith(“hello”) reads much better. Remember to give the user a clean API it’s worth creating some interfaces!

  11. Fredrik Yttergren

    @Jonatan
    I don’t really see that as much of a problem. The proposed solution on the blog already relies heavily on having an IDE to help you with autocomplete, and that same IDE will be just as helpful showing you the names and types of the constructor arguments (and probably javadoc too).

    Does it really read better though? The mandatory methods return types that have no obvious connections to the builder itself. When I use the Builder pattern, I expect to get a Builder in return with every call, so that I can keep building. If I don’t follow the suggested workflow and check autocomplete, I will probably wonder why the Builder returns a Name (or Port or whatever) when I start building. Builder is an established pattern by now, and this approach breaks the principle of least astonishment.

    Also, instead of comparing with new Something(“hello”, “name”), if you compare with new Something(greeting, userName) you haven’t really lost readability imo. It will read worse sometimes, that is true, but it’s also clearer that the Something class does indeed expect those 2 arguments before it can do it’s thing. builder().name(“name”) (if I’m just reading the code someone else wrote) doesn’t tell me that name is a mandatory argument at all, I only find that out if I try to call build() before I’m done with the mandatory methods. So even if it’s more aesthetically pleasing, you understand less of what’s going on, which makes it less readable.

  12. Fredrik Yttergren

    To illustrate the issue I have with the proposed API:
    Starting point: I want to build an Address
    1. I see that Address has no public constructors, but it does have a builder() method, so I assume that it’s the familiar Builder pattern I’m supposed to use.
    2. I look more closely and see that builder() actually returns a URL. Say what? I want to build an Address with a Builder. What does URL have to do with that?
    3. I look at the URL interface and see that it has a port() method that returns a Port. I still have no clue what’s going on. I’m trying to build an address and I’m getting weird classes that seem to have no real relation to any Builder that’s capable of building an Address (there’s no build() method in sight anywhere).

    Anyone who tries to use this API without having read about the “Builder pattern with a twist” first will probably be left having no idea what’s going on. The API is only clear to those who know how this modified pattern works, or those who follow a very specific workflow or check all the classes and interfaces of the API before they start using it (which is kinda like reading the whole manual for your new TV, how many actually do that?).

    Maybe I just have a weird approach to programming or something, but this API would definitely confuse me.

  13. Dean Pullen

    Almost exactly what I was after! The only problem I see with this pattern is that I’d only want to build an object with all given params and not need to force the user into providing optional params as null in the builder. Bloch’s pattern allows optional params to be added to the builder signature as needed, this always forces the user into providing them all (whether null or not).

  14. Dean Pullen

    By the way, Fredrik proposes a solution to my problem by using setters for optional params. But this defeats the purpose of using a builder in the way I need – immutability.

  15. Hi, thanks for your article. I just had the almost same idea, except yours is better because one builder implementation and several interfaces…

    Just to say that i implemented a java generator (based on jsr269) which generates a builder following your pattern, all from annotating a constructor.

    Please tell me what you think, the project is here : https://github.com/ltearno/builder-generator

  16. Tony

    There are a few comments which have specifically pointed out the fact that
    new Address.Builder.build() can be accomplished and in order to prevent the same some have advised to make the Builder constructor private. If I make the Builder class as a private static class then also I won’t be able to do new Address.Builder.build(). Can this be done?

Trackbacks for this post

  1. Builder pattern with a twist » CodeByExample
  2. Builder pattern for mandatory values | Aidium
  3. Improve builder pattern on validation check? | JAVA
  4. Design Pattern 1 – Builder | Ümit KÖSE

Leave a Reply