The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to generate different representations. This is particular useful when an object requires multiple components built in a specific sequence, and each component may have varying complexity. The pattern enables clients to construct objects without being aware of internal implementation details.
Key components of the Builder pattern include:
- Product: The final complex object being constructed.
- Builder: An interface defining methods for building individual parts of the product.
- Concrete Builder: Implements the Builder interface with specific logic for constructing the product and returns the final instance.
- Director: Orchestrates the construction steps by invoking methods on the Builder, but remains unaware of the product's internal structure.
- Client: Uses the Director and a Concrete Builder to assemble the final object.
Benefits of using this pattern:
- Encapsulation: Clients are insulated from the internal composition of the product.
- Extensibility: Concrete builders can be added independently, supporting easy system extension.
- Controlled Construction: The construction flow is managed centrally, reducing the risk of errors during object creation.
The core idea is to build complex objects step-by-step while allowing users to specify only the type and content of the desired object, hiding the underlying construction process.
In Java, implementing house construction via the Builder pattern involves several stages: defining the house (Product), declaring construction steps (Builder interface), providing concrete implementations (Concrete Builders), creating a director to manage the workflow, and finally using a client to initiate construction.
Here’s a simplified example:
1. Define the House Product
@Data
public class House {
private String foundation;
private String walls;
private String roof;
}
2. Define the Builder Interface
public interface HouseBuilder {
void buildFoundation();
void buildWalls();
void buildRoof();
House getResult();
}
3. Implement Concrete Builders
public class SmallHouseBuilder implements HouseBuilder {
private House house;
public SmallHouseBuilder() {
this.house = new House();
}
@Override
public void buildFoundation() {
System.out.println("Constructing foundation for small house...");
house.setFoundation("Small house foundation");
}
@Override
public void buildWalls() {
System.out.println("Constructing walls for small house...");
house.setWalls("Small house walls");
}
@Override
public void buildRoof() {
System.out.println("Constructing roof for small house...");
house.setRoof("Small house roof");
}
@Override
public House getResult() {
return house;
}
}
public class LargeHouseBuilder implements HouseBuilder {
private House house;
public LargeHouseBuilder() {
this.house = new House();
}
@Override
public void buildFoundation() {
System.out.println("Constructing foundation for large house...");
house.setFoundation("Large house foundation");
}
@Override
public void buildWalls() {
System.out.println("Constructing walls for large house...");
house.setWalls("Large house walls");
}
@Override
public void buildRoof() {
System.out.println("Constructing roof for large house...");
house.setRoof("Large house roof");
}
@Override
public House getResult() {
return house;
}
}
4. Create the Director
public class HouseConstructionManager {
private HouseBuilder builder;
public HouseConstructionManager(HouseBuilder builder) {
this.builder = builder;
}
public House construct() {
builder.buildFoundation();
builder.buildWalls();
builder.buildRoof();
return builder.getResult();
}
}
5. Client Code
public class Application {
public static void main(String[] args) {
HouseBuilder smallBuilder = new SmallHouseBuilder();
HouseConstructionManager manager = new HouseConstructionManager(smallBuilder);
House smallHouse = manager.construct();
System.out.println(smallHouse);
System.out.println("---");
HouseBuilder largeBuilder = new LargeHouseBuilder();
HouseConstructionManager manager2 = new HouseConstructionManager(largeBuilder);
House largeHouse = manager2.construct();
System.out.println(largeHouse);
}
}
Output
Constructing foundation for small house...
Constructing walls for small house...
Constructing roof for small house...
House(foundation=Small house foundation, walls=Small house walls, roof=Small house roof)
---
Constructing foundation for large house...
Constructing walls for large house...
Constructing roof for large house...
House(foundation=Large house foundation, walls=Large house walls, roof=Large house roof)
This demonstrates how the Builder pattern allows flexible and controlled object creation through modular, reusable components.