The Design Patterns Series: Builder Pattern
In this series, I want to take a look at a few of the well-known design patterns out there.
I will examine what each pattern is, what they try to help us with, and demonstrate each of them.
The first pattern I will be looking at in this series is the Builder pattern.
The Builder pattern is one of the twenty-three patterns described in the well-known “Design Patterns: Elements of Reusable Object-Oriented Software” book by the Gang of Four. It's referred to as a Creational pattern, as it is used to control class instantiation and seeks to help with the creation of complex objects.
The original Builder pattern described by the Gang of Four introduces the concept of a Product, Builder, Concrete Builder, and Director. The idea behind these concepts is that by abstracting the steps of object construction, varying Concrete Builders can be used to create different representations of an object.
I will not be focusing on the original Builder pattern described above, but on a version that I have typically seen used in a production environment. This sets out to help solve the problem of too many constructor parameters.
This version of the pattern is often referred to as Joshua Bloch's Builder Pattern, as it was first introduced by Joshua in the second edition of his book Effective Java.
Demonstration (C#)
Let’s take a look at the User
class below.
In the example constructor above I’m passing five parameters. Two of those parameters are optional.
You may also notice that I’m using getter-only auto-properties, so the properties are immutable. The only way for a client to set these properties is by passing them into the one available constructor.
What if I now want to add a set of new properties to the User
class above? I could add a PhoneNumber
, or Occupation
property. Do I extend the one constructor I currently have? In that case, do I set the new properties to be optional fields? I may not want to set these properties every time I construct this object.
I could take a number of approaches to constructing this object to help me solve these issues. For example, instead of having one constructor I could have multiple constructors that take the required parameters and N number of optional parameters:
While I only have seven properties to set in this example, the code above already starts to look a little unwieldy. The problem with this approach becomes even clearer If I decide to add more properties to our class and arguments to our constructor. For a client constructing this User
object, it could be confusing knowing which constructor to use. What defaults are set? With multiple properties of the same type was LastName
the second property or the last property?
Let's now look at how the Builder pattern can help:
The UserBuilder
class above has a single constructor that takes in the three required properties of our User object, then directly below the constructor
I have two methods that allow me to set the two optional properties. The UserBuilder
class has a fluent interface, which makes the client code easier to write and easier to read.
Let’s take a look at how a client would now construct the User
object to see the fluent interface and pattern in action:
Now you can see that the UserBuilder
only takes in the three required attributes – the two optional parameters are set by chaining on two further method calls. I could use the UserBuilder
to construct the User
object in just one line:
I could choose to just set the Email
property and not the Address
property:
By implementing the Builder pattern and fluent interface this gives me flexibility in constructing our objects and it makes the code easier to read.
One real-world scenario where I often see the Builder pattern used is unit tests. You may have a test that only cares about one property on a class but for the User
class above, you will have to set at least three properties and possibly two optional properties. Another example is where you just need an instance of that object and aren’t concerned with the data contained by that object.
Let’s look at how the Builder pattern and fluent interface could help in this case:
In the example above, I have set default values for all of the private fields in the UserBuilder
. This means that in my unit tests if I just care about the LastName
property, I can instantiate the UserBuilder
and chain the AddLastName
method with the data I wish to set for the LastName
property. The rest of the properties on the User
object will be populated with the default data.
If I don’t care about the specifics of the data contained by the User object and just want a User
object populated with default data, I could just call new UserBuilder
:
Conclusion
As with all of the above examples, I would want to go a step further and make them more robust by implementing error checking on each constructor and method.
Hopefully this post has given you some insight into how the Builder pattern can be applied in some situations to make your code easier to write and read.
Resources:
Creational Pattern
Fluent Interface
Builder Pattern
Getter-only auto-properties
Effective Java, 2nd Edition
Design Patterns: Elements of Reusable Object-Oriented Software