Skip to content

Commit

Permalink
feat: 新增文章
Browse files Browse the repository at this point in the history
  • Loading branch information
b1ngsha committed Jun 3, 2024
1 parent 4205839 commit 36b2021
Show file tree
Hide file tree
Showing 3 changed files with 387 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: AbstractFactory
date: 2024-05-02 23:14:24
tags:
- Design Patterns
- Factory
- Creational Patterns
categories: Design Patterns
---

Expand Down
385 changes: 385 additions & 0 deletions source/_posts/DesignPatterns/Builder.md
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());
}

}
```

Loading

0 comments on commit 36b2021

Please sign in to comment.