Builder Design Pattern Example
What is Builder pattern and what is it for? According to Design Patterns by GoF, the intent of Builder is to,
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
The key point here is the separation between the Builder (a complex object) and the Product (its representation). But why would you want such separation? The separation comes into help to make your code of Builder and Product each serves their own responsibility (Single Responsibility Principle).
If you haven't seen the sample code of Gof, please refer to the book first. The sample code Gof presented in their book is about building different kid of mazes. A maze consists of many rooms. A room connects to other rooms via walls and doors. Different mazes consists of different types of rooms, walls, and doors. Separating the Builder and the Product (maze) so you have a variety of MazeBuilders, each will create you maze with different rooms, walls, and doors. Without introducing a separate Builder class responsible for 'how to build' different kinds of maze, your Maze class will also be responsible for maze creation thus will be much more complex and less easy to understand. The separation is to lessen the complexity of the code so that the Builder knows how to build (create) an end Product, and the end Product class simply represents the product - a specific kind of maze which consists of different type of walls, rooms, doors, etc.
Don't worry if you're still a bit confused after reading Builder pattern in Gof book. I was too. In my own words, the intent of Builder is also to,
Separate the knowledge of 'how to create' an end Product from the end Product.
The inheritance hierarchy may exist on both sides (Builder class and Product class) in the above diagram for a more complex use case. Regardless inheritance or not, the simple separation of the building knowledge from the end concrete product class, and the capability to partially build (partially config) the end product until it is finally built at one fell swoop are enough to categorize this pattern as a Builder pattern.
A Builder design pattern is more than extracting out the code of Product creation. It has more advantages as a result of having a Builder class with its own responsibility - creating objects. Most commonly, you use constructor to create a concrete object. Alternatively, you call upon a class's Factory Method to return a concrete object. Since Builder can implicitly hold the 'configuration' information about an end Product (a concrete object), you can partially build the product by setting the configuration information until you're done with configuring then call the Builder's build() (or getResult()) method to create and/or return the end Product being built. To demonstrate this, I borrow the code excerpt from "Effective Java" book by Joshua Bloch which demonstrate the Builder pattern.
static nested/inner class
Use a static nested inner class when it is behaviorally a top-level class that has been nested in another top-level class for packaging convenience, and it does not need to access outer class's member. In java, declare an nested class static means its instance stands on its own so you can refer to it by its outer class name dot nested class name, while a private inner class can only be used by the outer class.
First up is the client code. As the client of the Builder, it is possible to have a Builder implemented in a way so the user can ask the builder to create an end product in one line of code:
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
The above client code nicely 'configures' the builder with required serving size and servering for a nutrition fact via Builder's constructor. From there, the line of code further configures the value of calories, sodium, and carbohydrate before calling build() to create and return an end Product as the last step. This syntactic sugar is neat to write, flexible to set configuration, and easy to be read. A single Builder can be used to build multiple objects by 'configure' the builder with different values. It also provides an elegant place to hold default configuration values via initializing private member variables with default values.
Below is the sample code of an end Product class named NutritionFacts which has a static inner Builder class, final fields, and a private constructor.
For example, NutritionFacts.Builder is a static inner class as seen in the above client code.
The final product you create from a Builder depends on the state of configuration held in the Builder. The above client code creates a NutritritionFacts for Coca Cola. To create another NutritionFacts for Pepsi, you'll use another instance of Builder and call its build() as the last step to create one. You see here the concrete class creation does not depend on calling different version of constructor of NutritionFacts. Instead, by calling build() on the different Builder with either Coca Cola configuration or Pepsi configuration get you result of a Coca Cola or Pepsi nutrition fact.
When you want to make a class immutable, Builder pattern can help. Applying Builder pattern helps to make a Product class immutable by extracting out mutable part into the Builder class, leaving Product class immutable.
In conclusion, apply Builder design pattern to your code if you want to:
Builder vs Abstract Factory:
They are quite similar with the following differences:
Buider vs Fluent Interface
Immutable vs mutable. See Comparing Builder Pattern and Fluent Interface.
Leave a Reply.