Программа курса← Объектно ориентированное программирование
1История ООП
2Объект и Класс
3Базовые концепции ООП
4Принцип проектирования GRASP
5Принцип проектирования SOLID
6Паттерны GOF

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);
    }
}
struct Car {
    std::string engine;
    int seats = 0;
    bool has_gps = false;
};

class ICarBuilder {
public:
    virtual void Reset() = 0;
    virtual void SetEngine(const std::string& engine) = 0;
    virtual void SetSeats(int number) = 0;
    virtual void SetGPS(bool has_gps) = 0;
    virtual Car GetResult() const = 0;
    virtual ~ICarBuilder() = default;
};

class CarBuilder : public ICarBuilder {
public:
    void Reset() override { car_ = Car{}; }
    void SetEngine(const std::string& engine) override { car_.engine = engine; }
    void SetSeats(int number) override { car_.seats = number; }
    void SetGPS(bool has_gps) override { car_.has_gps = has_gps; }
    Car GetResult() const override { return car_; }

private:
    Car car_;
};

class Director {
public:
    void ConstructSportsCar(ICarBuilder& builder) const {
        builder.Reset();
        builder.SetEngine("V8");
        builder.SetSeats(2);
        builder.SetGPS(true);
    }

    void ConstructCityCar(ICarBuilder& builder) const {
        builder.Reset();
        builder.SetEngine("V4");
        builder.SetSeats(4);
        builder.SetGPS(false);
    }
};
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 полезен, когда объект имеет сложную структуру и множество параметров. Он делает процесс создания гибким и контролируемым, упрощая изменение шагов построения и добавление новых вариантов конфигурации.