This chapter covers the builder pattern.
GoF Definition
Separate the construction of a complex object from its representation so that the same construction processes can create different representations.
Concept
The builder pattern is useful for creating complex objects that have multiple parts. The creational mechanism of an object should be independent of these parts. The construction process does not care about how these parts are assembled. The same construction process must allow us to create different representations of the objects.
Product is the complex object that you want to create. ConcreteBuilder constructs and assembles the parts of a product by implementing an abstract interface, Builder. The ConcreteBuilder objects build the product’s internal representations and define the creational process/assembling mechanisms. Builders can also provide methods to get an object that is created and available for use (notice the getVehicle() method in the Builder interface in the following implementation). Director is responsible for creating the final object using the Builder interface. In other words, Director uses Builder and controls the steps/sequence to build the final Product. Builders can also keep reference of the products that they built, so that they can be used again.
Real-World Example
To complete an order for a computer, different parts are assembled based on customer preferences (e.g., one customer can opt for a 500 GB hard disk with an Intel processor, and another customer can choose a 250 GB hard disk with an AMD processor). Consider another example. Suppose that you intend to go on a tour with a travel company that provides various packages for the same tour (for example, they can provide special arrangements, a different kind of vehicle for the sightseeing, etc.). You can choose your package based on your budget.
Computer-World Example
The builder pattern can be used when we want to convert one text format to another text format (e.g., RTF to ASCII text).
Note
The Java.util.Calendar.Builder class is an example in this category, but it is available in Java 8 and onward only. The java.lang.StringBuilder class is a close example in this context. But you need to remember that the GoF definition says that this pattern allows you to use the same construction process to make different representations. In this context, this example does not fully qualify for this pattern.
Illustration
In this example, we have the following participants: Builder, Car, MotorCycle, Product, and Director. The first three are very straightforward; Car and MotorCycle are concrete classes and they implement the Builder interface. Builder is used to create parts of the Product object, where Product represents the complex object under construction.
Since Car and MotorCycle are the concrete implementations of the Builder interface, they need to implement the methods that are declared in the Builder interface. That’s why they needed to supply the body for the startUpOperations() , buildBody() , insertWheels() , addHeadlights() , endOperations() , and getVehicle()methods . The first five methods are straightforward; they are used to perform an operation at the beginning (or end), build the body of the vehicle, insert the wheels, and add headlights. getVehicle() returns the final product. In this case, Director is responsible for constructing the final representation of these products using the Builder interface. (See the structure defined by the GoF). Notice that Director is calling the same construct() method to create different types of vehicles.
Now go through the code to see how different parts are assembled for this pattern.
Class Diagram
Package Explorer View
Implementation
Output
Q&A Session
- 1.What are the advantages of using a builder pattern?
You can create a complex object, step by step, and vary the steps. You promote encapsulation by hiding the details of the complex construction process. The director can retrieve the final product from the builder when the whole construction is over. In general, at a high level, you seem to have only one method that makes the complete product. Other internal methods only involve partial creation. So, you have finer control over the construction process.
Using this pattern, the same construction process can produce different products.
Since you can vary the construction steps, you can vary product’s internal representation.
- 2.What are the drawbacks/pitfalls associated with the builder pattern?
It is not suitable if you want to deal with mutable objects (which can be modified later).
You may need to duplicate some portion of the code. These duplications may have significant impact in some context and turn into an antipattern.
A concrete builder is dedicated to a particular type of product. So, to create different type of products, you may need to come up with different concrete builders.
The approach makes more sense if the structure is very complex.
- 3.
Can I use an abstract class instead of the interface in the illustration of this pattern?
Yes. You can use an abstract class instead of an interface in this example.
- 4.
How do I decide whether I should use an abstract class or an interface in an application?
I believe that if you want to have some centralized or default behavior, the abstract class is a better choice. In those cases, you can provide some default implementation. On the other hand, interface implementation starts from scratch. They indicate some kind of rules/contracts on what is to be done (e.g., you must implement the method) but they will not enforce the how part of it. Also, interfaces are preferred when you are trying to implement the concept of multiple inheritance.
But at the same time, if you need to add a new method in an interface, then you need to track down all the implementations of that interface and you need to put the concrete implementation for that method in all of those places. You can add a new method in an abstract class with a default implementation and the existing code can run smoothly.
Java has taken special care with this last point. Java 8 introduced the use of ‘default’ keyword in the interface. You can prefix the default word before the intended method signature and provide a default implementation. Interface methods are public by default, so you do not need to mark it with the keyword public.
These summarized suggestions are from the Oracle Java documentation at https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html .
You should prefer the abstract class in the following scenarios:You want to share code among multiple closely related classes.
The classes that extend the abstract class can have many common methods or fields, or they require non-public access modifiers inside them.
You want to use non-static or/and non-final fields, which enables us to define methods that can access and modify the state of the object to which they belong.
On the other hand, you should prefer interfaces for these scenarios:You expect that several unrelated classes are going to implement your interface (e.g., comparable interface can be implemented by many unrelated classes).
You specify the behavior of a particular data type, but it does not matter how the implementer implements that.
You want to use the concept of multiple inheritance in your application.
Note
In my book Interactive Object-Oriented Programming in Java (Apress, 2016), I discussed abstract classes, interfaces, and the use of the “default” keyword with various examples and outputs. Refer to that book for a detailed discussion and analysis.
- 5.
I am seeing that in cars, model names are added in the beginning, but for motorcycles, model names are added at the end. Is it intentional?
Yes. It was used to demonstrate the fact that each of the concrete builders can decide how they want to produce the final products. They have this freedom.
- 6.
Why are you using a separate class for director? You could use the client code to play the role of the director.
No one restricts you to do that. In the preceding implementation, I wanted to separate this role from the client code in the implementation. But in the upcoming/modified implementation, I have used the client as a director.
- 7.
What do you mean by client code ?
The class that contains the main() method is the client code. In most parts of the book, client code means the same.
- 8.
You mentioned varying steps several times. Can you demonstrate an implementation where the final product is created with different variations and steps?
Good catch. You asked for a demonstration of the real power of the builder pattern. So, let us consider another example.
Modified Illustration
In this modified implementation, I consider only cars as the final products.
I create custom cars that have the following attributes: a start-up message (startUpMessage), a process completion message (endOperationsMessage), the body material of the car (bodyType), the number of wheels on the car (noOfWheels), and the number of headlights (noOfHeadLights) on the car.
The client code is playing the role of a director in this implementation.
I have renamed the builder interface as ModifiedBuilder. Apart from the constructCar() and getConstructedCar() methods , each of the methods in the interface has the ModifiedBuilder return type, which helps us to apply method chaining mechanism in the client code.
Modified Package Explorer View
Modified Implementation
Modified Output
Analysis
- 9.
I am seeing the use of final keywords in client codes. But you have not used those for ProductClass attributes . What is the reason for that?
In the client code, I used the final keywords to promote immutability. But in the ProductClass class, the attributes are already marked with private keywords and there are no setter methods, so these are already immutable.
- 10.
What is the key benefit of immutable objects?
Once constructed, they can be safely shared, and most importantly, they are thread safe, so you save lots in synchronization costs in a multithreaded environment.
- 11.
When should I consider using a builder pattern?
If you need to make a complex object that involves various steps in the construction process, and at the same time, the products need to be immutable, the builder pattern is a good choice.