Что такое паттерн Builder и в чём его преимущества?

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

Ответ

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

Проблема, которую решает Builder:

  • «Телескопический конструктор» — множество перегруженных версий конструктора с разными наборами параметров.
  • Конструктор с большим количеством параметров, где легко перепутать порядок аргументов, особенно если они одного типа.
  • Создание неизменяемых (immutable) объектов, когда все поля должны быть final.

Реализация (классический Builder на Java):

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

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

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

        public Builder(String cpu, String ram) {
            this.cpu = cpu;
            this.ram = ram;
        }

        // Сеттеры для необязательных полей (возвращают this для fluent-интерфейса)
        public Builder storage(String storage) {
            this.storage = storage;
            return this;
        }

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

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

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

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

  1. Читаемость и ясность: Именованные сеттеры (storage("...")) делают код самодокументируемым.
  2. Гибкость: Можно легко задавать только нужные опциональные параметры в любом порядке.
  3. Неизменяемость: Объект после build() становится immutable (все поля final), что безопасно для многопоточности.
  4. Валидация: Логику проверки корректности параметров можно централизовать в методе build().
  5. Поддержка обязательных полей: Они задаются через конструктор Builder, что гарантирует их наличие.

Альтернативы в Java: Для классов с множеством полей также можно использовать Lombok (@Builder) или реализацию Builder'а через records (Java 16+).