Зачем нужен паттерн Builder?

Ответ

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

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

  • Телескопический конструктор: Конструктор с десятком параметров, где большинство — null или значения по умолчанию, запутан и подвержен ошибкам.
  • Неизменяемость (immutability): Если объект должен быть immutable после создания, нельзя использовать сеттеры. Builder позволяет сконфигурировать все поля перед финальным созданием.

Преимущества в использовании:

  • Читаемость: Цепочка методов (builder.setName("X").setPower(100).build()) явно показывает, что конфигурируется.
  • Гибкость: Можно создавать разные вариации объекта, используя один и тот же процесс построения.
  • Изоляция сложности: Логика валидации параметров и сборки объекта скрыта внутри Builder'а.

Пример реализации на Java для конфигурации HTTP-клиента:

public class HttpClient {
    private final String baseUrl;
    private final int timeout;
    private final boolean useCache;
    // Приватный конструктор, вызывается только Builder'ом
    private HttpClient(Builder builder) {
        this.baseUrl = builder.baseUrl;
        this.timeout = builder.timeout;
        this.useCache = builder.useCache;
    }

    public static class Builder {
        private String baseUrl = "http://localhost"; // Значение по умолчанию
        private int timeout = 5000;
        private boolean useCache = false;

        public Builder baseUrl(String url) { this.baseUrl = url; return this; }
        public Builder timeout(int ms) { this.timeout = ms; return this; }
        public Builder useCache(boolean flag) { this.useCache = flag; return this; }

        public HttpClient build() {
            // Можно добавить валидацию параметров здесь
            if (timeout <= 0) throw new IllegalArgumentException("Timeout must be positive");
            return new HttpClient(this);
        }
    }
}

// Использование
HttpClient client = new HttpClient.Builder()
        .baseUrl("https://api.example.com")
        .timeout(10000)
        .useCache(true)
        .build();

Ответ 18+ 🔞

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

В чём, собственно, засада, которую он решает:

  • Конструктор-монстр: Ты пытаешься запихнуть в один конструктор 15 параметров. Получается пиздец: new Pizza(true, false, null, 100, "Margarita", ...). Через неделю сам не поймёшь, что это за true на первом месте, а коллега, который это увидит, просто скажет "иди нахуй" и уйдёт. Это чистая манда с ушами.
  • Неизменяемость — это круто: Если объект после создания менять нельзя (а это часто нужно), то сеттеры отпадают. А как тогда задать параметры? Вот Builder и есть выход — накрутил всё, что надо, в билдере, а потом раз — build(), и готовый, неизменяемый объект.

Почему он — огонь:

  • Читаемость — пиздец: Цепочка методов builder.setBaseUrl(...).setTimeout(...) читается как книга. Сразу видно, что и куда ты пишешь. Никакой ебалы с порядком аргументов.
  • Гибкость — хоть обосрись: Хочешь объект с одним набором полей, хочешь — с другим. Процесс сборки один, а вариации — любые.
  • Сложность — в жопу: Вся логика, типа проверок (валидации), спрятана внутри метода build(). Клиентский код чистенький, не засран.

Смотри, как это выглядит на Java для какого-нибудь HTTP-клиента:

public class HttpClient {
    private final String baseUrl;
    private final int timeout;
    private final boolean useCache;
    // Конструктор приватный, чтобы никто, кроме Builder'а, не лез
    private HttpClient(Builder builder) {
        this.baseUrl = builder.baseUrl;
        this.timeout = builder.timeout;
        this.useCache = builder.useCache;
    }

    // Сам Builder — статический класс внутри
    public static class Builder {
        private String baseUrl = "http://localhost"; // Дефолтное значение
        private int timeout = 5000;
        private boolean useCache = false;

        // Методы возвращают this, чтобы можно было чейнить
        public Builder baseUrl(String url) { this.baseUrl = url; return this; }
        public Builder timeout(int ms) { this.timeout = ms; return this; }
        public Builder useCache(boolean flag) { this.useCache = flag; return this; }

        // Финальный аккорд — создание объекта
        public HttpClient build() {
            // Тут можно проверить, что всё ок. Например:
            if (timeout <= 0) throw new IllegalArgumentException("Timeout must be positive, дурачок!");
            return new HttpClient(this);
        }
    }
}

// Использование — красота же!
HttpClient client = new HttpClient.Builder()
        .baseUrl("https://api.example.com")
        .timeout(10000)
        .useCache(true)
        .build(); // Всё, готово!

Вот и вся магия. Вместо того чтобы ебеться с конструкторами на 20 параметров или плодить кучу подклассов, ты просто используешь Builder. Код становится понятным, как божий день, и доверия к нему — больше. Просто иногда нужно э бошка думай, прежде чем городить конструктор-монстра.