Lapa Knowledge Base
Порождающие паттерны

Builder

Паттерн Builder

Builder — это порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово. Builder даёт возможность использовать один и тот же код строительства для получения разных представлений объектов.

Проблема

Представьте сложный объект, требующий пошаговой инициализации множества полей и вложенных объектов. Такой код обычно инициализируется внутри перегруженного конструктора с десятком параметров, либо ещё хуже — размазан по всему клиентскому коду.

Например, давайте подумаем о том, как создать объект House. Чтобы построить простой дом, нужно возвести четыре стены, установить двери, вставить пару окон и положить крышу. Но что если вы хотите дом побольше и светлее, с садом и бассейном?

Самое простое решение — расширить базовый класс House и создать набор подклассов для покрытия всех комбинаций параметров. Но в итоге получится множество подклассов. Любой новый параметр, например, тип материала для крыши, заставит вас растиражировать это иерархию классов.

Есть и другой подход. Можно не распылять логику создания по классам, а вынести её в один огромный конструктор базового класса House, который будет принимать все возможные параметры и управлять созданием объекта. Хотя такой подход и избавляет от необходимости создавать подклассы, он создаёт другую проблему.

Решение

Паттерн Builder предлагает вынести конструирование объекта за пределы его собственного класса, поручив это дело отдельным объектам, называемым строителями.


Структура

Builder
├── reset(): void
├── setSeats(number): void
├── setEngine(engine): void
├── setTripComputer(): void
└── setGPS(): void

ConcreteBuilder
├── product: Product
├── reset(): void
├── setSeats(number): void
├── setEngine(engine): void
├── setTripComputer(): void
├── setGPS(): void
└── getProduct(): Product

Director
├── builder: Builder
├── setBuilder(builder): void
└── constructSportsCar(): void

Product
└── parts: string[]

Пример реализации

JavaScript

// Продукт
class Car {
    constructor() {
        this.parts = [];
    }
    
    addPart(part) {
        this.parts.push(part);
    }
    
    listParts() {
        console.log(`Части автомобиля: ${this.parts.join(', ')}`);
    }
}

// Интерфейс строителя
class CarBuilder {
    reset() {
        throw new Error('Метод reset должен быть переопределен');
    }
    
    setSeats(number) {
        throw new Error('Метод setSeats должен быть переопределен');
    }
    
    setEngine(engine) {
        throw new Error('Метод setEngine должен быть переопределен');
    }
    
    setTripComputer() {
        throw new Error('Метод setTripComputer должен быть переопределен');
    }
    
    setGPS() {
        throw new Error('Метод setGPS должен быть переопределен');
    }
}

// Конкретный строитель
class ConcreteCarBuilder extends CarBuilder {
    constructor() {
        super();
        this.car = null;
    }
    
    reset() {
        this.car = new Car();
    }
    
    setSeats(number) {
        this.car.addPart(`Сиденья: ${number}`);
    }
    
    setEngine(engine) {
        this.car.addPart(`Двигатель: ${engine}`);
    }
    
    setTripComputer() {
        this.car.addPart('Компьютер поездки');
    }
    
    setGPS() {
        this.car.addPart('GPS навигация');
    }
    
    getProduct() {
        const result = this.car;
        this.reset();
        return result;
    }
}

// Директор
class Director {
    constructor() {
        this.builder = null;
    }
    
    setBuilder(builder) {
        this.builder = builder;
    }
    
    constructSportsCar() {
        this.builder.reset();
        this.builder.setSeats(2);
        this.builder.setEngine('V8');
        this.builder.setTripComputer();
        this.builder.setGPS();
    }
    
    constructSUV() {
        this.builder.reset();
        this.builder.setSeats(7);
        this.builder.setEngine('V6');
        this.builder.setGPS();
    }
}

// Использование
const director = new Director();
const builder = new ConcreteCarBuilder();

director.setBuilder(builder);

// Создаем спортивный автомобиль
director.constructSportsCar();
const sportsCar = builder.getProduct();
sportsCar.listParts();

// Создаем SUV
director.constructSUV();
const suv = builder.getProduct();
suv.listParts();

// Создаем автомобиль вручную
builder.reset();
builder.setSeats(4);
builder.setEngine('I4');
builder.setGPS();
const manualCar = builder.getProduct();
manualCar.listParts();

TypeScript с интерфейсами

// Продукт
class Computer {
    private parts: string[] = [];
    
    addPart(part: string): void {
        this.parts.push(part);
    }
    
    listParts(): void {
        console.log(`Компоненты компьютера: ${this.parts.join(', ')}`);
    }
}

// Интерфейс строителя
interface ComputerBuilder {
    reset(): void;
    setCPU(cpu: string): void;
    setRAM(ram: string): void;
    setStorage(storage: string): void;
    setGPU(gpu: string): void;
}

// Конкретный строитель
class GamingComputerBuilder implements ComputerBuilder {
    private computer: Computer;
    
    constructor() {
        this.computer = new Computer();
    }
    
    reset(): void {
        this.computer = new Computer();
    }
    
    setCPU(cpu: string): void {
        this.computer.addPart(`CPU: ${cpu}`);
    }
    
    setRAM(ram: string): void {
        this.computer.addPart(`RAM: ${ram}`);
    }
    
    setStorage(storage: string): void {
        this.computer.addPart(`Storage: ${storage}`);
    }
    
    setGPU(gpu: string): void {
        this.computer.addPart(`GPU: ${gpu}`);
    }
    
    getProduct(): Computer {
        const result = this.computer;
        this.reset();
        return result;
    }
}

// Директор
class ComputerDirector {
    private builder: ComputerBuilder;
    
    setBuilder(builder: ComputerBuilder): void {
        this.builder = builder;
    }
    
    buildGamingPC(): void {
        this.builder.reset();
        this.builder.setCPU('Intel i9-12900K');
        this.builder.setRAM('32GB DDR5');
        this.builder.setStorage('1TB NVMe SSD');
        this.builder.setGPU('RTX 4080');
    }
    
    buildOfficePC(): void {
        this.builder.reset();
        this.builder.setCPU('Intel i5-12400');
        this.builder.setRAM('16GB DDR4');
        this.builder.setStorage('512GB SSD');
        this.builder.setGPU('Integrated Graphics');
    }
}

// Использование
const director = new ComputerDirector();
const builder = new GamingComputerBuilder();

director.setBuilder(builder);

director.buildGamingPC();
const gamingPC = builder.getProduct();
gamingPC.listParts();

director.buildOfficePC();
const officePC = builder.getProduct();
officePC.listParts();

Когда использовать

Используйте Builder, когда:

  • Нужно создавать сложные объекты с множеством параметров
  • Хотите создавать разные представления одно
  • го объекта
  • Нужно избежать перегруженных конструкторов
  • Объект должен быть создан пошагово
  • Нужна гибкость в процессе создания объекта

Преимущества

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

Недостатки

  • Усложняет код из-за введения дополнительных классов
  • Может быть избыточным для простых объектов

Отличия от других паттернов

  • Factory Method — создает один тип продукта
  • Abstract Factory — создает семейства связанных объектов
  • Prototype — создает объекты путем клонирования

Реальный пример

// Строитель для создания HTML элементов
class HTMLElementBuilder {
    constructor(tagName) {
        this.element = document.createElement(tagName);
        this.attributes = {};
        this.children = [];
    }
    
    addClass(className) {
        this.element.classList.add(className);
        return this;
    }
    
    setAttribute(name, value) {
        this.element.setAttribute(name, value);
        return this;
    }
    
    setText(text) {
        this.element.textContent = text;
        return this;
    }
    
    addChild(child) {
        this.children.push(child);
        return this;
    }
    
    build() {
        this.children.forEach(child => {
            this.element.appendChild(child);
        });
        return this.element;
    }
}

// Использование
const button = new HTMLElementBuilder('button')
    .addClass('btn')
    .addClass('btn-primary')
    .setAttribute('type', 'submit')
    .setText('Отправить')
    .build();

const form = new HTMLElementBuilder('form')
    .setAttribute('action', '/submit')
    .setAttribute('method', 'POST')
    .addChild(button)
    .build();

Builder — это идеальный паттерн для создания сложных объектов пошагово!

Copyright © 2026