Будівельник — це породжуючий патерн проектування, який дозволяє створювати об’єкти покроково.
На відміну від інших породжуючих патернів, Будівельник дозволяє виготовляти різні продукти, використовуючи один і той же процес будівництва.
Складність:
Популярність:
Застосування: Патерн можна часто зустріти в Java-коді, особливо там, де необхідним є покрокове створення продуктів або конфігурація складних об’єктів.
Патерн широко використовується в стандартних бібліотеках Java:
Ознаки застосування патерна: Будівельника можна визначити у класі, який має один створюючий метод та декілька методів налаштування створюваного продукту. Зазвичай, для зручності, методи налаштувань викликають ланцюжком (наприклад, someBuilder.setValueA(1).setValueB(2).create()
).
Покрокове виробництво автомобілів
У цьому прикладі Будівельник дозволяє покроково конструювати різні конфігурації автомобілів.
Крім того, тут показано, як за допомогою Будівельника можна створити інший продукт, використовуючи ті самі кроки будівництва. Для цього у нас є два конкретних будівельника — CarBuilder
та CarManualBuilder
.
Порядок будівництва продуктів укладено в Директора. Він знає, які кроки будівельників потрібно викликати, щоб отримати ту чи іншу конфігурацію продукту. Він працює з будівельниками тільки через загальний інтерфейс, що дозволяє взаємозамінювати об’єкти будівельників для отримання різних продуктів.
builders builders/Builder.java: Загальний інтерфейс будівельників
package refactoring_guru.builder.example.builders;
import refactoring_guru.builder.example.cars.CarType;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* 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: Будівельник автомобілів
package refactoring_guru.builder.example.builders;
import refactoring_guru.builder.example.cars.Car;
import refactoring_guru.builder.example.cars.CarType;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* 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;
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: Будівельник посібників користувача
package refactoring_guru.builder.example.builders;
import refactoring_guru.builder.example.cars.Manual;
import refactoring_guru.builder.example.cars.CarType;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* 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);
}
}
cars cars/Car.java: Продукт-автомобіль
package refactoring_guru.builder.example.cars;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* 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: Продукт-посібник
package refactoring_guru.builder.example.cars;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* 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;
}
}
cars/CarType.java
package refactoring_guru.builder.example.cars;
public enum CarType {
CITY_CAR, SPORTS_CAR, SUV
}
components components/Engine.java: Різні фічі продуктів
package refactoring_guru.builder.example.components;
/**
* Just another feature of a car.
*/
public class Engine {
private final double volume;
private double mileage;
private boolean started;
public Engine(double volume, double mileage) {
this.volume = volume;
this.mileage = mileage;
}
public void on() {
started = true;
}
public void off() {
started = false;
}
public boolean isStarted() {
return started;
}
public void go(double mileage) {
if (started) {
this.mileage += mileage;
} else {
System.err.println("Cannot go(), you must start engine first!");
}
}
public double getVolume() {
return volume;
}
public double getMileage() {
return mileage;
}
}
components/GPSNavigator.java: Різні фічі продуктів
package refactoring_guru.builder.example.components;
/**
* Just another feature of a car.
*/
public class GPSNavigator {
private String route;
public GPSNavigator() {
this.route = "221b, Baker Street, London to Scotland Yard, 8-10 Broadway, London";
}
public GPSNavigator(String manualRoute) {
this.route = manualRoute;
}
public String getRoute() {
return route;
}
}
components/Transmission.java: Різні фічі продуктів
package refactoring_guru.builder.example.components;
/**
* Just another feature of a car.
*/
public enum Transmission {
SINGLE_SPEED, MANUAL, AUTOMATIC, SEMI_AUTOMATIC
}
components/TripComputer.java: Різні фічі продуктів
package refactoring_guru.builder.example.components;
import refactoring_guru.builder.example.cars.Car;
/**
* Just another feature of a car.
*/
public class TripComputer {
private Car car;
public void setCar(Car car) {
this.car = car;
}
public void showFuelLevel() {
System.out.println("Fuel level: " + car.getFuel());
}
public void showStatus() {
if (this.car.getEngine().isStarted()) {
System.out.println("Car is started");
} else {
System.out.println("Car isn't started");
}
}
}
director director/Director.java: Директор керує будівельниками
package refactoring_guru.builder.example.director;
import refactoring_guru.builder.example.builders.Builder;
import refactoring_guru.builder.example.cars.CarType;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* 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());
}
}
Demo.java: Клієнтський код
package refactoring_guru.builder.example;
import refactoring_guru.builder.example.builders.CarBuilder;
import refactoring_guru.builder.example.builders.CarManualBuilder;
import refactoring_guru.builder.example.cars.Car;
import refactoring_guru.builder.example.cars.Manual;
import refactoring_guru.builder.example.director.Director;
/**
* 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());
}
}
OutputDemo.txt: Результати виконання
Car built:
SPORTS_CAR
Car manual built:
Type of car: SPORTS_CAR
Count of seats: 2
Engine: volume - 3.0; mileage - 0.0
Transmission: SEMI_AUTOMATIC
Trip Computer: Functional
GPS Navigator: Functional