Builder (Строитель)

Паттерн Builder (Строитель) используется для пошагового создания сложных объектов. Он отделяет конструирование объекта от его представления, позволяя использовать один и тот же процесс строительства для разных представлений объекта.

Когда применять Builder

  • Когда нужно создавать сложные объекты, состоящие из множества частей.
  • Когда нужно изолировать код сборки объекта от его внутренней структуры.
  • Когда необходимо обеспечить различные представления создаваемого объекта.

PlantUML-диаграмма

UML диаграмма Builder

public class Car
{
    public string Engine { get; set; }
    public int Seats { get; set; }
    public bool HasGPS { get; set; }

    public override string ToString()
    {
        return $"Автомобиль: двигатель = {Engine}, сидений = {Seats}, GPS = {HasGPS}";
    }
}

// Интерфейс строителя
public interface ICarBuilder
{
    void Reset();
    void SetEngine(string engine);
    void SetSeats(int number);
    void SetGPS(bool hasGPS);
    Car GetResult();
}

// Конкретный строитель
public class CarBuilder : ICarBuilder
{
    private Car _car = new Car();

    public void Reset() => _car = new Car();
    public void SetEngine(string engine) => _car.Engine = engine;
    public void SetSeats(int number) => _car.Seats = number;
    public void SetGPS(bool hasGPS) => _car.HasGPS = hasGPS;
    public Car GetResult() => _car;
}

// Директор
public class Director
{
    public void ConstructSportsCar(ICarBuilder builder)
    {
        builder.Reset();
        builder.SetEngine("V8");
        builder.SetSeats(2);
        builder.SetGPS(true);
    }

    public void ConstructCityCar(ICarBuilder builder)
    {
        builder.Reset();
        builder.SetEngine("V4");
        builder.SetSeats(4);
        builder.SetGPS(false);
    }
}

// Использование
class Program
{
    static void Main()
    {
        var director = new Director();
        var builder = new CarBuilder();

        director.ConstructSportsCar(builder);
        Car sportsCar = builder.GetResult();
        Console.WriteLine(sportsCar);

        director.ConstructCityCar(builder);
        Car cityCar = builder.GetResult();
        Console.WriteLine(cityCar);
    }
}
type Car struct {
	Engine string
	Seats  int
	HasGPS bool
}

func (c Car) String() string {
	return fmt.Sprintf("Автомобиль: двигатель = %s, сидений = %d, GPS = %t", c.Engine, c.Seats, c.HasGPS)
}

type CarBuilder interface {
	Reset()
	SetEngine(engine string)
	SetSeats(number int)
	SetGPS(hasGPS bool)
	Result() Car
}

type carBuilder struct {
	car Car
}

func (b *carBuilder) Reset()                  { b.car = Car{} }
func (b *carBuilder) SetEngine(engine string) { b.car.Engine = engine }
func (b *carBuilder) SetSeats(number int)     { b.car.Seats = number }
func (b *carBuilder) SetGPS(hasGPS bool)      { b.car.HasGPS = hasGPS }
func (b *carBuilder) Result() Car             { return b.car }

type Director struct{}

func (Director) ConstructSportsCar(b CarBuilder) {
	b.Reset()
	b.SetEngine("V8")
	b.SetSeats(2)
	b.SetGPS(true)
}

func (Director) ConstructCityCar(b CarBuilder) {
	b.Reset()
	b.SetEngine("V4")
	b.SetSeats(4)
	b.SetGPS(false)
}

func main() {
	director := Director{}
	builder := &carBuilder{}

	director.ConstructSportsCar(builder)
	fmt.Println(builder.Result())

	director.ConstructCityCar(builder)
	fmt.Println(builder.Result())
}
from abc import ABC, abstractmethod
from dataclasses import dataclass


@dataclass
class Car:
    engine: str = ""
    seats: int = 0
    has_gps: bool = False

    def __str__(self) -> str:
        return f"Автомобиль: двигатель = {self.engine}, сидений = {self.seats}, GPS = {self.has_gps}"


class CarBuilder(ABC):
    @abstractmethod
    def reset(self) -> None: ...

    @abstractmethod
    def set_engine(self, engine: str) -> None: ...

    @abstractmethod
    def set_seats(self, number: int) -> None: ...

    @abstractmethod
    def set_gps(self, has_gps: bool) -> None: ...

    @abstractmethod
    def result(self) -> Car: ...


class SimpleCarBuilder(CarBuilder):
    def __init__(self) -> None:
        self.reset()

    def reset(self) -> None:
        self._car = Car()

    def set_engine(self, engine: str) -> None:
        self._car.engine = engine

    def set_seats(self, number: int) -> None:
        self._car.seats = number

    def set_gps(self, has_gps: bool) -> None:
        self._car.has_gps = has_gps

    def result(self) -> Car:
        return self._car


class Director:
    def construct_sports_car(self, builder: CarBuilder) -> None:
        builder.reset()
        builder.set_engine("V8")
        builder.set_seats(2)
        builder.set_gps(True)

    def construct_city_car(self, builder: CarBuilder) -> None:
        builder.reset()
        builder.set_engine("V4")
        builder.set_seats(4)
        builder.set_gps(False)


director = Director()
builder = SimpleCarBuilder()

director.construct_sports_car(builder)
print(builder.result())

director.construct_city_car(builder)
print(builder.result())
class Car {
    engine = "";
    seats = 0;
    hasGPS = false;

    toString(): string {
        return `Автомобиль: двигатель = ${this.engine}, сидений = ${this.seats}, GPS = ${this.hasGPS}`;
    }
}

interface CarBuilder {
    reset(): void;
    setEngine(engine: string): void;
    setSeats(number: number): void;
    setGPS(hasGPS: boolean): void;
    getResult(): Car;
}

class SimpleCarBuilder implements CarBuilder {
    private car = new Car();

    reset(): void {
        this.car = new Car();
    }

    setEngine(engine: string): void {
        this.car.engine = engine;
    }

    setSeats(number: number): void {
        this.car.seats = number;
    }

    setGPS(hasGPS: boolean): void {
        this.car.hasGPS = hasGPS;
    }

    getResult(): Car {
        return this.car;
    }
}

class Director {
    constructSportsCar(builder: CarBuilder): void {
        builder.reset();
        builder.setEngine("V8");
        builder.setSeats(2);
        builder.setGPS(true);
    }

    constructCityCar(builder: CarBuilder): void {
        builder.reset();
        builder.setEngine("V4");
        builder.setSeats(4);
        builder.setGPS(false);
    }
}

const director = new Director();
const builder = new SimpleCarBuilder();

director.constructSportsCar(builder);
console.log(String(builder.getResult()));

director.constructCityCar(builder);
console.log(String(builder.getResult()));
class Car {
    String engine;
    int seats;
    boolean hasGPS;

    @Override
    public String toString() {
        return "Автомобиль: двигатель = " + engine + ", сидений = " + seats + ", GPS = " + hasGPS;
    }
}

interface CarBuilder {
    void reset();
    void setEngine(String engine);
    void setSeats(int number);
    void setGPS(boolean hasGPS);
    Car getResult();
}

class SimpleCarBuilder implements CarBuilder {
    private Car car = new Car();

    public void reset() {
        car = new Car();
    }

    public void setEngine(String engine) {
        car.engine = engine;
    }

    public void setSeats(int number) {
        car.seats = number;
    }

    public void setGPS(boolean hasGPS) {
        car.hasGPS = hasGPS;
    }

    public Car getResult() {
        return car;
    }
}

class Director {
    void constructSportsCar(CarBuilder builder) {
        builder.reset();
        builder.setEngine("V8");
        builder.setSeats(2);
        builder.setGPS(true);
    }

    void constructCityCar(CarBuilder builder) {
        builder.reset();
        builder.setEngine("V4");
        builder.setSeats(4);
        builder.setGPS(false);
    }
}

public class Program {
    public static void main(String[] args) {
        Director director = new Director();
        CarBuilder builder = new SimpleCarBuilder();

        director.constructSportsCar(builder);
        System.out.println(builder.getResult());

        director.constructCityCar(builder);
        System.out.println(builder.getResult());
    }
}
data class Car(
    var engine: String = "",
    var seats: Int = 0,
    var hasGPS: Boolean = false,
) {
    override fun toString() = "Автомобиль: двигатель = $engine, сидений = $seats, GPS = $hasGPS"
}

interface CarBuilder {
    fun reset()
    fun setEngine(engine: String)
    fun setSeats(number: Int)
    fun setGPS(hasGPS: Boolean)
    fun result(): Car
}

class SimpleCarBuilder : CarBuilder {
    private var car = Car()

    override fun reset() {
        car = Car()
    }

    override fun setEngine(engine: String) {
        car.engine = engine
    }

    override fun setSeats(number: Int) {
        car.seats = number
    }

    override fun setGPS(hasGPS: Boolean) {
        car.hasGPS = hasGPS
    }

    override fun result(): Car = car
}

class Director {
    fun constructSportsCar(builder: CarBuilder) {
        builder.reset()
        builder.setEngine("V8")
        builder.setSeats(2)
        builder.setGPS(true)
    }

    fun constructCityCar(builder: CarBuilder) {
        builder.reset()
        builder.setEngine("V4")
        builder.setSeats(4)
        builder.setGPS(false)
    }
}

fun main() {
    val director = Director()
    val builder = SimpleCarBuilder()

    director.constructSportsCar(builder)
    println(builder.result())

    director.constructCityCar(builder)
    println(builder.result())
}

Плюсы и минусы

Плюсы Минусы
Позволяет создавать сложные объекты пошагово Усложняет код при простых структурах объектов
Изолирует код сборки от представления объекта Может потребовать дополнительных классов-строителей
Позволяет использовать один и тот же процесс для разных объектов Увеличивает объём кода из-за добавления директора

Заключение

Builder полезен, когда объект имеет сложную структуру и множество параметров. Он делает процесс создания гибким и контролируемым, упрощая изменение шагов построения и добавление новых вариантов конфигурации.