Builder is a creational design pattern that lets you produce different types and representations of an object using the same building process. Builder allows constructing complex objects step by step.
Imagine a complex object that requires laborious step by step initialization of many fields and nested objects. Such code is usually buried inside a constructor with lots of parameters, or worse, scattered all over the client code.
For example, let's think how to create a
House object. To build a simple house, you need to construct four walls, install a door and two windows, build a roof. But what if you want a bigger, brighter house, with a backyard and other goodies?
The simplest solution that comes to mind is to extend the base
House class and to create subclasses to cover all combinations of the parameters. The obvious problem with this approach is a considerable number of subclasses you will end up with. Any new parameter, such as porch style, will require growing this hierarchy even more.
On the other hand, you can create a giant constructor in the base
House class that accepts all possible parameters of resulting house. While this approach will indeed eliminate the need for sublcasses, it will also create another problem.
Some of these parameters will be unused most of the time, making constructors calls pretty ugly. For instance, most houses do not have a swimming pool. Therefore, all parameters related to swimming pools will be useless in 99% of cases.
The Builder pattern suggests to extract the object construction code out of its own class and move it to separate objects called builders.
The pattern organizes the object construction into a set of steps (such as
buildDoor, etc.) To create an object, you will need to call several building steps in a builder class. The important part here is that you do not need to call all the steps. You can use only the steps necessary for producing a particular configuration of an object.
Often, the same construction step will be different for various products that you are trying to build. For example, a cabin may be build with wooden logs, but a castle must be build with stone.
In this case, you can create several different builder classes that implement the same building steps in a different manner. Then you can use these builders in the same construction process to produce different kinds of objects.
For example, there is a
StoneBuilder class that makes everything from stone and a
WoodBuilder class that uses wood. You could pass one of them through a set of construction steps to produce a house built either from stone or wood. This would work only if the client code that calls building steps interacts with builders using their common interface.
You can go even further and extract the whole construction process to a special class called
Director. In this case, the
Director will be defining the order of building steps, and a
Builder object, linked with the
Director, will execute them. This structure will hide any construction details from client code.
Builder declares steps required to build a product.
Concrete Builders provide different implementations of the construction steps. Builders can also provide methods for fetching a construction result. This method can not be declared inside the builder interface, because builders may produce products that do not have the common interface. But if you are dealing with products from a single hierarchy, then this method can be safely described in the base interface.
Product is an object created as a result of construction. Builders can produce products that do not belong to the same class hierarchy or interface. It is a key difference between the Builder and other creational patterns.
Director constructs products using a Builder object. Usually, client assigns a builder instance to a director just once via constructor parameters. Then director uses that single builder object for all further construction. But there is an alternative when a builder is passed to the main production method of a director.
Note, that Builder does not require creating a director class. The separate director class is handy when you have several product variations that require different construction processes. Director can encapsulate all that code inside a single class.
This example illustrates how Builder is used for the step-by-step construction of cars. The director class produces various models of cars using a different set of construction steps. Director works with a build object that was given to him. This allows you to reuse existing construction code for producing user manuals for various car models. To achieve this, you just need to a new builder class.
Therefore, the Builder design pattern allows you to create different variations of products, simply by changing the amount and order of the construction steps. Also, you can produce an entirely different type of product using the same construction code, by submitting an alternative version of a builder object.
When you have a "telescopic" constructor.
A constructor with a dozen of optional parameters is not convenient to call. You have to specify all of the parameters, even if you do not need them.
To ease the pain, one can overload a long constructor and create several shorter versions with fewer parameters. They will still call the main constructor, but pass some default values into omitted parameters.
The Builder pattern allows building objects step by step. Moreover, you can use only required steps and skip the optional ones when building a simple object.
When your code has to create different representations of one product (for example, stone and wooden houses). Construction of the product has similar steps that differ in details. Plus, although the products may be similar, they do not necessary have to have a common base class or interface.
Builder can be used to construct different products using the same building process.
Each distinct product will be represented by a separate builder class. Code that controls the construction order may live in a single director class.
When you have to build a Composite tree or another complex object.
Builder constructs products steps by step. It allows deferred or even recursive building that is mandatory when you are working with tree structures. Builder does not expose unfinished product while running construction steps. This prevents client code from getting corrupted results.
How to Implement
Make sure that you have the common steps of building the product, as well as variations of the steps that lead to the creation of various representations of products.
Create the Builder interface and declare production steps in it.
Create a Concrete Builder class for each of the product representations. Implement their construction steps.
Think about creating a Director class. Its methods should create different product configurations, using different steps of the same builder instance.
The client code creates both Builder and Director objects. It creates a builder instance first and then passes it either to the director's constructor or its production methods.
The client should call a production method of a Director object to begin the construction process.
The result can be obtained from the Director object only if all products have a common interface. In the opposite case, each Builder must have its own method of retrieving the result.
Pros and Cons
- Allows building products step by step.
- Allows using the same code for building different products.
- Isolates the complex construction code from a product's core business logic.
- Increases overall code complexity by creating multiple additional classes.
Relations with Other Patterns
Often, designs start out using Factory Method (less complicated, more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complex) as the designer discovers where more flexibility is needed.
Builder focuses on constructing a complex object step by step. Abstract Factory creates families of product objects (either simple or complex). Builder returns the product as a final step, but the Abstract Factory returns the result immediately.