-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
387 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,385 @@ | ||
--- | ||
title: Builder | ||
date: 2024-06-03 23:41:55 | ||
tags: | ||
- Design Patterns | ||
- Creational Patterns | ||
categories: Design Patterns | ||
--- | ||
**Builder** is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code. | ||
|
||
<!--more--> | ||
|
||
<br> | ||
|
||
# Problem | ||
|
||
Imagine a complex object that requires laborious, step-by-step initialization of many fields and nested objects. Such initialization code is usually buried inside a monstrous constructor with lots of parameters. Or even worse: scattered all over the client code. | ||
|
||
For example. let's think about how to create a `House` object. To build a simple house, you need to construct four walls and a floor, install a door, fit a pair of windows, and build a roof. But what if you want a bigger, brighter house, with a backyard and other goodies? | ||
The simplest solution is to extend the base `House` class and create a set of subclasses to cover all combinations of the parameters. But eventually you'll end up with a considerable number of subclasses. Any new parameter, such as the porch style, will require growing this hierarchy even more. | ||
|
||
There's another approach that doesn't involve breeding subclasses. You can create a giant constructor right in the base `House` class with all possible parameters that control the house object. While this approach indeed eliminates the need for subclasses, it creates another problem. | ||
|
||
![The constructor with lots of parameters has its downside: not all the parameters are needed at all times.](https://b1ngsha-blog.oss-cn-beijing.aliyuncs.com/image-20240603231333780.png) | ||
|
||
In most cases most of the parameters will be unused, making the constructor calls pretty ugly. For instance, only a fraction of houses have swimming pools, so the parameters related to swimming pools will be useless nine times out of ten. | ||
|
||
---- | ||
|
||
|
||
|
||
# Solution | ||
|
||
The Builder pattern suggests that you extract the object construction code out of its own class and move it to separate objects called `builders`. | ||
|
||
![The Builder pattern lets you construct complex objects step by step. The Builder doesn't allow other objects to access the product while it's being built.](https://b1ngsha-blog.oss-cn-beijing.aliyuncs.com/image-20240603231737063.png) | ||
|
||
The pattern organizes object construction into a set of steps (`buildWalls`, `buildDoor`, etc.). To create an object, you execute a series of these steps on a builder object. The important part is that you don’t need to call all of the steps. You can call only those steps that are necessary for producing a particular configuration of an object. | ||
|
||
Some of the construction steps might require different implementation when you need to build various representations of the product. For example, walls of a cabin may be built of wood, but the castle walls must be built with stone. | ||
|
||
In this case, you can create several different builder classes that implement the same set of building steps, but in a different manner. Then you can use these builders in the construction process (i.e., an ordered set of calls to the building steps) to produce different kinds of objects. | ||
|
||
<br> | ||
|
||
## Director | ||
|
||
You can go further and extract a series of calls to the builder steps you use to construct a product into a separate class called *director*. The director class defines the order in which to execute the building steps, while the builder provides the implementation for those steps. | ||
|
||
![The director knows which building steps to execute to get a working product.](https://b1ngsha-blog.oss-cn-beijing.aliyuncs.com/image-20240603232601252.png) | ||
|
||
Having a director class in your program isn’t strictly necessary. You can always call the building steps in a specific order directly from the client code. However, the director class might be a good place to put various construction routines so you can reuse them across your program. | ||
|
||
In addition, the director class completely hides the details of product construction from the client code. The client only needs to associate a builder with a director, launch the construction with the director, and get the result from the builder. | ||
|
||
---- | ||
|
||
|
||
|
||
# Builder in Java | ||
|
||
In this example, the Builder pattern allows step by step construction of different car models. | ||
|
||
The example also shows how Builder produces products of different kinds (car manual) using the same building steps. | ||
|
||
The Director controls the order of the construction. It knows which building steps to call to produce this or that car model. It works with builders only via their common interface. This allows passing different types of builders to the director. | ||
|
||
The end result is retrieved from the builder object because the director can’t know the type of resulting product. Only the Builder object knows what does it build exactly. | ||
|
||
<br> | ||
|
||
**builders** | ||
|
||
**builders/Builder.java: Common builder interface** | ||
|
||
```java | ||
/** | ||
* Builder interface defines all possible ways to configure a product. | ||
*/ | ||
public interface Builder { | ||
void setCarType(CarType type); | ||
void setSeats(int seats); | ||
void setEngine(Engine engine); | ||
void setTransmission(Transmission transmission); | ||
void setTripComputer(TripComputer tripComputer); | ||
void setGPSNavigator(GPSNavigator gpsNavigator); | ||
} | ||
``` | ||
|
||
**builders/CarBuilder.java: Builder of car** | ||
|
||
```java | ||
/** | ||
* Concrete builders implement steps defined in the common interface. | ||
*/ | ||
public class CarBuilder implements Builder { | ||
private CarType type; | ||
private int seats; | ||
private Engine engine; | ||
private Transmission transmission; | ||
private TripComputer tripComputer; | ||
private GPSNavigator gpsNavigator; | ||
|
||
@Override | ||
public void setCarType(CarType type) { | ||
this.type = type; | ||
} | ||
|
||
@Override | ||
public void setSeats(int seats) { | ||
this.seats = seats; | ||
} | ||
|
||
@Override | ||
public void setEngine(Engine engine) { | ||
this.engine = engine; | ||
} | ||
|
||
@Override | ||
public void setTransmission(Transmission transmission) { | ||
this.transmission = transmission; | ||
} | ||
|
||
@Override | ||
public void setTripComputer(TripComputer tripComputer) { | ||
this.tripComputer = tripComputer; | ||
} | ||
|
||
@Override | ||
public void setGPSNavigator(GPSNavigator gpsNavigator) { | ||
this.gpsNavigator = gpsNavigator; | ||
} | ||
|
||
public Car getResult() { | ||
return new Car(type, seats, engine, transmission, tripComputer, gpsNavigator); | ||
} | ||
} | ||
``` | ||
|
||
**builders/CarManualBuilder.java: Builder of a car manual** | ||
|
||
```java | ||
/** | ||
* Unlike other creational patterns, Builder can construct unrelated products, | ||
* which don't have the common interface. | ||
* | ||
* In this case we build a user manual for a car, using the same steps as we | ||
* built a car. This allows to produce manuals for specific car models, | ||
* configured with different features. | ||
*/ | ||
public class CarManualBuilder implements Builder{ | ||
private CarType type; | ||
private int seats; | ||
private Engine engine; | ||
private Transmission transmission; | ||
private TripComputer tripComputer; | ||
private GPSNavigator gpsNavigator; | ||
|
||
@Override | ||
public void setCarType(CarType type) { | ||
this.type = type; | ||
} | ||
|
||
@Override | ||
public void setSeats(int seats) { | ||
this.seats = seats; | ||
} | ||
|
||
@Override | ||
public void setEngine(Engine engine) { | ||
this.engine = engine; | ||
} | ||
|
||
@Override | ||
public void setTransmission(Transmission transmission) { | ||
this.transmission = transmission; | ||
} | ||
|
||
@Override | ||
public void setTripComputer(TripComputer tripComputer) { | ||
this.tripComputer = tripComputer; | ||
} | ||
|
||
@Override | ||
public void setGPSNavigator(GPSNavigator gpsNavigator) { | ||
this.gpsNavigator = gpsNavigator; | ||
} | ||
|
||
public Manual getResult() { | ||
return new Manual(type, seats, engine, transmission, tripComputer, gpsNavigator); | ||
} | ||
} | ||
``` | ||
|
||
<br> | ||
|
||
**cars** | ||
|
||
**cars/Car.java: Car product** | ||
|
||
```java | ||
/** | ||
* Car is a product class. | ||
*/ | ||
public class Car { | ||
private final CarType carType; | ||
private final int seats; | ||
private final Engine engine; | ||
private final Transmission transmission; | ||
private final TripComputer tripComputer; | ||
private final GPSNavigator gpsNavigator; | ||
private double fuel = 0; | ||
|
||
public Car(CarType carType, int seats, Engine engine, Transmission transmission, | ||
TripComputer tripComputer, GPSNavigator gpsNavigator) { | ||
this.carType = carType; | ||
this.seats = seats; | ||
this.engine = engine; | ||
this.transmission = transmission; | ||
this.tripComputer = tripComputer; | ||
if (this.tripComputer != null) { | ||
this.tripComputer.setCar(this); | ||
} | ||
this.gpsNavigator = gpsNavigator; | ||
} | ||
|
||
public CarType getCarType() { | ||
return carType; | ||
} | ||
|
||
public double getFuel() { | ||
return fuel; | ||
} | ||
|
||
public void setFuel(double fuel) { | ||
this.fuel = fuel; | ||
} | ||
|
||
public int getSeats() { | ||
return seats; | ||
} | ||
|
||
public Engine getEngine() { | ||
return engine; | ||
} | ||
|
||
public Transmission getTransmission() { | ||
return transmission; | ||
} | ||
|
||
public TripComputer getTripComputer() { | ||
return tripComputer; | ||
} | ||
|
||
public GPSNavigator getGpsNavigator() { | ||
return gpsNavigator; | ||
} | ||
} | ||
``` | ||
|
||
**cars/Manual.java: Manual product** | ||
|
||
```java | ||
/** | ||
* Car manual is another product. Note that it does not have the same ancestor | ||
* as a Car. They are not related. | ||
*/ | ||
public class Manual { | ||
private final CarType carType; | ||
private final int seats; | ||
private final Engine engine; | ||
private final Transmission transmission; | ||
private final TripComputer tripComputer; | ||
private final GPSNavigator gpsNavigator; | ||
|
||
public Manual(CarType carType, int seats, Engine engine, Transmission transmission, | ||
TripComputer tripComputer, GPSNavigator gpsNavigator) { | ||
this.carType = carType; | ||
this.seats = seats; | ||
this.engine = engine; | ||
this.transmission = transmission; | ||
this.tripComputer = tripComputer; | ||
this.gpsNavigator = gpsNavigator; | ||
} | ||
|
||
public String print() { | ||
String info = ""; | ||
info += "Type of car: " + carType + "\n"; | ||
info += "Count of seats: " + seats + "\n"; | ||
info += "Engine: volume - " + engine.getVolume() + "; mileage - " + engine.getMileage() + "\n"; | ||
info += "Transmission: " + transmission + "\n"; | ||
if (this.tripComputer != null) { | ||
info += "Trip Computer: Functional" + "\n"; | ||
} else { | ||
info += "Trip Computer: N/A" + "\n"; | ||
} | ||
if (this.gpsNavigator != null) { | ||
info += "GPS Navigator: Functional" + "\n"; | ||
} else { | ||
info += "GPS Navigator: N/A" + "\n"; | ||
} | ||
return info; | ||
} | ||
} | ||
``` | ||
|
||
<br> | ||
|
||
**director** | ||
|
||
**director/Director.java: Director controls builders** | ||
|
||
```java | ||
/** | ||
* Director defines the order of building steps. It works with a builder object | ||
* through common Builder interface. Therefore it may not know what product is | ||
* being built. | ||
*/ | ||
public class Director { | ||
|
||
public void constructSportsCar(Builder builder) { | ||
builder.setCarType(CarType.SPORTS_CAR); | ||
builder.setSeats(2); | ||
builder.setEngine(new Engine(3.0, 0)); | ||
builder.setTransmission(Transmission.SEMI_AUTOMATIC); | ||
builder.setTripComputer(new TripComputer()); | ||
builder.setGPSNavigator(new GPSNavigator()); | ||
} | ||
|
||
public void constructCityCar(Builder builder) { | ||
builder.setCarType(CarType.CITY_CAR); | ||
builder.setSeats(2); | ||
builder.setEngine(new Engine(1.2, 0)); | ||
builder.setTransmission(Transmission.AUTOMATIC); | ||
builder.setTripComputer(new TripComputer()); | ||
builder.setGPSNavigator(new GPSNavigator()); | ||
} | ||
|
||
public void constructSUV(Builder builder) { | ||
builder.setCarType(CarType.SUV); | ||
builder.setSeats(4); | ||
builder.setEngine(new Engine(2.5, 0)); | ||
builder.setTransmission(Transmission.MANUAL); | ||
builder.setGPSNavigator(new GPSNavigator()); | ||
} | ||
} | ||
``` | ||
|
||
<br> | ||
|
||
**Demo.java: Client code** | ||
|
||
```java | ||
/** | ||
* Demo class. Everything comes together here. | ||
*/ | ||
public class Demo { | ||
|
||
public static void main(String[] args) { | ||
Director director = new Director(); | ||
|
||
// Director gets the concrete builder object from the client | ||
// (application code). That's because application knows better which | ||
// builder to use to get a specific product. | ||
CarBuilder builder = new CarBuilder(); | ||
director.constructSportsCar(builder); | ||
|
||
// The final product is often retrieved from a builder object, since | ||
// Director is not aware and not dependent on concrete builders and | ||
// products. | ||
Car car = builder.getResult(); | ||
System.out.println("Car built:\n" + car.getCarType()); | ||
|
||
|
||
CarManualBuilder manualBuilder = new CarManualBuilder(); | ||
|
||
// Director may know several building recipes. | ||
director.constructSportsCar(manualBuilder); | ||
Manual carManual = manualBuilder.getResult(); | ||
System.out.println("\nCar manual built:\n" + carManual.print()); | ||
} | ||
|
||
} | ||
``` | ||
|
Oops, something went wrong.