Last Updated on September 24, 2023 by KnownSense
The Builder Design pattern is a design approach that lets you create complex objects step by step. It enables producing various types and structures of an object using the same building process.
Think about a complicated object that needs to be set up carefully, with many small details and parts. Usually, this setup code is put into a big constructor with lots of information. Or sometimes, it’s spread out everywhere in the client code.
Let’s consider creating a House object. To make a basic house, you’d need to build four walls, a floor, a door, a couple of windows, and a roof. But what if you’re aiming for a larger, more feature-rich house, complete with a backyard and additional amenities like heating, plumbing, and electrical systems?
The simplest solution might involve extending the main House class and generating a bunch of subclasses to handle different combinations of features. However, as you keep adding more options, the number of subclasses can quickly grow. Introducing a new feature, like a specific type of porch, would further increase this hierarchy.
Alternatively, there’s another approach that doesn’t require creating numerous subclasses. You can opt to create an extensive constructor within the base House class, including parameters for all the potential features that a house might have. While this approach does eliminate the need for multiple subclasses, it introduces another issue.
But the constructor with an excessive number of parameters has its downsides: not all the parameters are necessary all the time. In most cases, many of the parameters remain unused, making constructor calls appear quite messy. For instance, only a fraction of houses will include a swimming pool. As a result, parameters related to swimming pools will be irrelevant in nine out of ten cases.
We can resolve this issue by extracting the object construction code out of its own class and move it to separate objects called builders.
Benefits
- Clear Object Creation: The Builder pattern separates the construction of a complex object from its representation, making the creation process clearer and more organized.
- Step-by-Step Construction: It allows objects to be constructed step by step, controlling the order and completeness of each step, which is especially useful for complex objects with multiple attributes.
- Parameter Flexibility: Builders enable the configuration of object properties with different parameters, accommodating variations without the need to create numerous constructor overloads.
- Avoids Telescoping Constructors: The Builder pattern prevents the proliferation of constructors with multiple parameters, which can be confusing and error-prone, by centralizing the parameter handling.
- Readability and Maintainability: Code using the Builder pattern tends to be more readable and maintainable, as each step of the construction process is explicit and isolated.
- Code Reusability: Builders can be reused across different objects and scenarios, promoting code reuse and reducing duplication.
- Simplifies Complex Object Creation: For objects with intricate initialization or configuration, the Builder pattern simplifies the process by abstracting away the complexity.
- Consistency in Object Creation: Builders ensure that objects are created in a consistent manner, avoiding situations where objects might be in an invalid or incomplete state.
- Improved Client Code: The client code becomes cleaner and more intuitive, as it only needs to focus on specifying the desired attributes through a clear interface.
- Flexibility in Representation: The Builder pattern allows for the creation of multiple representations of the same object, which can be useful when dealing with different use cases or platforms.
- Encapsulation of Construction Logic: The construction logic is encapsulated within the Builder, reducing the chance of errors and making it easier to change the construction process without affecting the client code.
- Supports Fluent Interface: Builders often employ a fluent interface, enabling method chaining for a more readable and expressive way of constructing objects.
UML for Builder Pattern
The Builder design pattern consists of several key components that work together to facilitate the creation of complex objects. Here are the main components:
- Builder Interface: This is an interface that declares methods for building different parts of the complex object. It defines a set of abstract methods that each concrete builder must implement. This interface provides the blueprint for building the final object.
- Concrete Builders: Concrete builder classes implement the methods defined in the builder interface. Each concrete builder is responsible for constructing a specific variation or configuration of the complex object. It assembles the parts and keeps track of the object being built.
- Director: The director class orchestrates the construction process using a specific builder. It provides a higher-level interface for constructing objects by using the builder’s methods. The director guides the builder step by step to create the final object.
- Product: The product is the complex object that’s being constructed. It’s the object with multiple parts or attributes that are gradually built by the builder. The product doesn’t have to be directly created by the client; it’s the result of the builder’s work.
- Client: The client code is responsible for creating a director, selecting a specific builder, and then using the director to construct the complex object. The client code specifies the desired features or attributes of the final object.
Implementation
In this implementation, we will create a builder for constructing houses.
Step1: Create a class House with different parameters like walls, roof, windows, door , floor, pool etc.
class House {
private String walls;
private String roof;
private String windows;
private String doors;
private int floor;
private String pool;
public void setWalls(String walls) {
this.walls = walls;
}
public void setRoof(String roof) {
this.roof = roof;
}
public void setWindows(String windows) {
this.windows = windows;
}
public void setDoors(String doors) {
this.doors = doors;
}
// ... other getters and methods
}
Step2: Create interface HouseBuilder with methods to create different components of House
interface HouseBuilder {
void buildWalls();
void buildRoof();
void buildWindows();
void buildDoors();
void buildFloor();
void buildPool();
House getResult();
}
Step3: Create a concrete class SimpleHouseBuilder.
class SimpleHouseBuilder implements HouseBuilder {
private House house;
public SimpleHouseBuilder() {
this.house = new House();
}
@Override
public void buildWalls() {
house.setWalls("Basic walls");
}
@Override
public void buildRoof() {
house.setRoof("Simple roof");
}
@Override
public void buildWindows() {
house.setWindows("Standard windows");
}
@Override
public void buildDoors() {
house.setDoors("Regular doors");
}
@Override
public House getResult() {
return house;
}
}
Step4: Similarly , we can create other classes like multiFloorHouse, poolHouse etc.
Step5: Create a director class, the actual builder class responsible to create House objects.
class HouseDirector {
private HouseBuilder builder;
public HouseDirector(HouseBuilder builder) {
this.builder = builder;
}
public void construct() {
builder.buildWalls();
builder.buildRoof();
builder.buildWindows();
builder.buildDoors();
}
}
Step6: The main class uses HosuseDirector to demonstrate builder pattern.
public class Main {
public static void main(String[] args) {
HouseBuilder builder = new SimpleHouseBuilder();
HouseDirector director = new HouseDirector(builder);
director.construct();
House house = builder.getResult();
System.out.println("House construction complete:");
System.out.println("Walls: " + house.getWalls());
System.out.println("Roof: " + house.getRoof());
System.out.println("Windows: " + house.getWindows());
System.out.println("Doors: " + house.getDoors());
}
}
OUTPUT:
House construction complete:
Walls: Basic walls
Roof: Simple roof
Windows: Standard windows
Doors: Regular doors
Conclusion
In conclusion, the Builder design pattern is a valuable tool in object-oriented programming for creating complex objects with varying configurations while promoting code clarity and maintainability. By separating the construction process from the object’s representation, the pattern allows for step-by-step construction and flexibility in handling different object variations.
By adopting the Builder pattern, developers can efficiently manage the creation of intricate objects, maintain a structured codebase, and facilitate future scalability and changes in object construction requirements.