← Назад к вопросам

Какие плюсы и минусы паттерна Builder?

2.0 Middle🔥 141 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерн Builder: плюсы и минусы

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

Плюсы Builder

1. Читаемость кода

Сравните конструктор с множеством параметров и Builder:

// ❌ Сложно читать и ошибиться легко
User user = new User("John", "john@example.com", 30, true, "USA", "engineer", "2020-01-15");

// ✅ Ясно и понятно
User user = new User.Builder()
    .name("John")
    .email("john@example.com")
    .age(30)
    .active(true)
    .country("USA")
    .role("engineer")
    .joinDate("2020-01-15")
    .build();

2. Опциональные параметры

Легко задавать только нужные параметры, остальные получают дефолтные значения:

User user = new User.Builder("John", "john@example.com")
    .age(30)
    .build();

3. Неизменяемость (Immutability)

Объект создаётся целиком, а не изменяется по частям. Это безопасно для многопоточности:

public class User {
    private final String name;
    private final String email;
    private final int age;
    
    // Нет сеттеров!
    
    public static class Builder {
        private String name;
        private String email;
        private int age = 0;
        
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        
        public User build() {
            return new User(this);
        }
    }
}

4. Валидация на этапе build()

Можно проверить все условия перед созданием объекта:

public User build() {
    if (name == null || name.isEmpty()) {
        throw new IllegalArgumentException("Name is required");
    }
    if (!email.contains("@")) {
        throw new IllegalArgumentException("Invalid email");
    }
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("Invalid age");
    }
    return new User(this);
}

5. Fluent Interface

Цепочка вызовов выглядит естественно и лаконично:

Order order = new Order.Builder()
    .customer("Alice")
    .addItem("Laptop", 999.99)
    .addItem("Mouse", 25.99)
    .shippingAddress("123 Main St")
    .express(true)
    .build();

Минусы Builder

1. Больше кода

Нужно писать дополнительный класс Builder, методы-сеттеры. Для простых объектов это оверинжиниринг:

// Для простого Person(String name) писать Builder — излишне
public class Point {
    private final int x;
    private final int y;
    
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

// Builder здесь — избыточность
Point p = new Point.Builder().x(10).y(20).build();
// Проще: Point p = new Point(10, 20);

2. Медленнее обычного конструктора

Больше объектов создаётся (Builder + сам объект), больше вызовов методов:

// На каждый вызов метода — дополнительный overhead
User user = new User.Builder()
    .name("John")       // 1 вызов + сохранение в Builder
    .email("john@...")  // 2 вызов
    .age(30)            // 3 вызов
    .build();           // 4 вызов + создание User

// Против: new User("John", "john@...", 30);

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

3. Сложность в многопоточности

Если Builder не потокобезопасен и вы передаёте его между потоками, могут быть race conditions:

// ❌ Опасно
Builder builder = new User.Builder();
new Thread(() -> builder.name("Alice")).start();
new Thread(() -> builder.email("alice@...")).start();
User user = builder.build(); // Какие значения?

4. Легко забыть обязательные поля

Если нет проверок в build(), можно создать невалидный объект:

// ❌ Вызовет NullPointerException потом
User user = new User.Builder().build();

// ✅ Нужна валидация
public User build() {
    if (name == null) throw new IllegalStateException("Name is required");
    return new User(this);
}

5. Не подходит для DTO и простых data classes

Для Lombok @Builder или record это не проблема, но старый код требует много писания:

// ✅ Современный подход с Lombok
@Data
@Builder
public class User {
    private String name;
    private String email;
    private int age;
}

User user = User.builder()
    .name("John")
    .email("john@example.com")
    .age(30)
    .build();

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

СценарийИспользуй Builder?
Простой объект (2-3 поля)Нет, обычный конструктор
Много опциональных параметровДа
Комплексная валидацияДа
Иммутабельность критичнаДа
Fluent API важен для читаемостиДа
DTO с LombokДа (@Builder)
Критична микросекундная производительностьМожет, обычный конструктор

Пример правильного использования

@Data
@Builder
public class DatabaseConnection {
    @Builder.Default
    private String host = "localhost";
    
    @Builder.Default
    private int port = 5432;
    
    private String database;
    private String username;
    private String password;
    
    @Builder.Default
    private int timeout = 30;
    
    @Builder.Default
    private int maxConnections = 10;
}

// Использование
DatabaseConnection conn = DatabaseConnection.builder()
    .database("mydb")
    .username("admin")
    .password("secret")
    .maxConnections(50)
    .build();

Вывод

Builder — мощный паттерн для создания сложных объектов с опциональными параметрами. Используй его для сущностей с 4+ полями или когда нужна сложная валидация. Для простых DTO применяй Lombok @Builder. Избегай Builder для тривиальных случаев — это добавляет ненужную сложность.