Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?
like image 385
tangens Avatar asked Oct 28 '09 17:10

tangens


People also ask

What problem does the builder pattern solve?

The Builder design pattern solves problems like: How can a class (the same construction process) create different representations of a complex object? How can a class that includes creating a complex object be simplified?

When would you apply the builder design pattern?

This pattern should be used: When it's necessary to use a constructor with a long parameter list or when there's a long list of constructors with different parameters. When it's necessary to build different representations of the same object.


1 Answers

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.

like image 184
Michael Borgwardt Avatar answered Sep 29 '22 11:09

Michael Borgwardt