Что такое паттерн проектирования Builder (Строитель) и в чём его преимущества?

«Что такое паттерн проектирования Builder (Строитель) и в чём его преимущества?» — вопрос из категории Паттерны, который задают на 22% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Решаемая проблема

Избегание "телескопического конструктора" (множества перегруженных версий с разными наборами параметров) и необходимости передавать null для неиспользуемых полей.

Классическая реализация на Java

public class Computer {
    // Обязательные параметры (final)
    private final String cpu;
    private final String ram;
    // Необязательные параметры
    private final String storage;
    private final String gpu;

    // Приватный конструктор, принимает Builder
    private Computer(ComputerBuilder builder) {
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.storage = builder.storage;
        this.gpu = builder.gpu;
    }

    // Статический вложенный класс Builder
    public static class ComputerBuilder {
        // Обязательные параметры
        private final String cpu;
        private final String ram;
        // Необязательные параметры с значениями по умолчанию
        private String storage = "256GB SSD";
        private String gpu = "Integrated";

        // Конструктор Builder с обязательными параметрами
        public ComputerBuilder(String cpu, String ram) {
            this.cpu = cpu;
            this.ram = ram;
        }

        // Методы для установки необязательных параметров (возвращают this для fluent API)
        public ComputerBuilder storage(String storage) {
            this.storage = storage;
            return this;
        }

        public ComputerBuilder gpu(String gpu) {
            this.gpu = gpu;
            return this;
        }

        // Финальный метод build() создает целевой объект
        public Computer build() {
            // Здесь можно добавить валидацию параметров
            if (cpu == null || ram == null) {
                throw new IllegalArgumentException("CPU and RAM are required");
            }
            return new Computer(this);
        }
    }
}

// Использование (Fluent Interface):
Computer myPC = new Computer.ComputerBuilder("Intel i7", "16GB DDR4")
        .storage("1TB NVMe")
        .gpu("NVIDIA RTX 4070")
        .build(); // Создается неизменяемый объект Computer

Ключевые преимущества

  1. Читаемость и ясность: Имена методов (storage(), gpu()) делают код самодокументируемым.
  2. Гибкость: Позволяет создавать объекты с разными комбинациями параметров, не загромождая класс множеством конструкторов.
  3. Неизменяемость (Immutability): Целевой объект (Computer) может быть сделан неизменяемым (final поля), что повышает надежность в многопоточных средах.
  4. Валидация: Проверку корректности параметров можно централизованно выполнить в методе build() перед созданием объекта.

Альтернативы в современном Java

  • Lombok @Builder: Генерирует код Builder автоматически.
  • Record с Builder (в комбинации с библиотеками): Для создания неизменяемых DTO.