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

Что такое шаблон Builder?

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

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

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

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

Паттерн Builder

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

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

Представьте класс с множеством параметров:

public class UserProfile {
    private String firstName;        // Обязательно
    private String lastName;         // Обязательно
    private String email;            // Обязательно
    private String phone;            // Опционально
    private String address;          // Опционально
    private LocalDate birthDate;     // Опционально
    private boolean newsletter;      // Опционально
    private String bio;              // Опционально
    private List<String> hobbies;    // Опционально
    private String avatar;           // Опционально
    
    // Как создать конструктор?
    // public UserProfile(String firstName, String lastName, String email) { ... }  // 3 параметра
    // public UserProfile(String firstName, String lastName, String email, String phone) { ... }  // 4
    // public UserProfile(String firstName, String lastName, String email, String phone, String address) { ... }  // 5
    // ... это адское расширение (Telescoping Constructor Anti-pattern)
}

Решение: паттерн Builder

public class UserProfile {
    private final String firstName;
    private final String lastName;
    private final String email;
    private final String phone;
    private final String address;
    private final LocalDate birthDate;
    private final boolean newsletter;
    private final String bio;
    private final List<String> hobbies;
    private final String avatar;
    
    // Приватный конструктор - создание только через Builder
    private UserProfile(Builder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.email = builder.email;
        this.phone = builder.phone;
        this.address = builder.address;
        this.birthDate = builder.birthDate;
        this.newsletter = builder.newsletter;
        this.bio = builder.bio;
        this.hobbies = builder.hobbies;
        this.avatar = builder.avatar;
    }
    
    // Вложенный класс Builder
    public static class Builder {
        private final String firstName;         // Обязательные параметры
        private final String lastName;
        private final String email;
        
        private String phone = null;            // Опциональные - с дефолтами
        private String address = null;
        private LocalDate birthDate = null;
        private boolean newsletter = false;
        private String bio = null;
        private List<String> hobbies = new ArrayList<>();
        private String avatar = null;
        
        // Конструктор для обязательных параметров
        public Builder(String firstName, String lastName, String email) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.email = email;
        }
        
        // Методы для установки опциональных параметров
        public Builder phone(String phone) {
            this.phone = phone;
            return this;  // Возвращаем this для chain
        }
        
        public Builder address(String address) {
            this.address = address;
            return this;
        }
        
        public Builder birthDate(LocalDate birthDate) {
            this.birthDate = birthDate;
            return this;
        }
        
        public Builder newsletter(boolean subscribe) {
            this.newsletter = subscribe;
            return this;
        }
        
        public Builder bio(String bio) {
            this.bio = bio;
            return this;
        }
        
        public Builder hobbies(List<String> hobbies) {
            this.hobbies = hobbies;
            return this;
        }
        
        public Builder addHobby(String hobby) {
            this.hobbies.add(hobby);
            return this;
        }
        
        public Builder avatar(String avatar) {
            this.avatar = avatar;
            return this;
        }
        
        // Метод для создания финального объекта
        public UserProfile build() {
            return new UserProfile(this);
        }
    }
    
    // Getters
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
    public String getPhone() { return phone; }
    public String getAddress() { return address; }
    public LocalDate getBirthDate() { return birthDate; }
    public boolean isNewsletter() { return newsletter; }
    public String getBio() { return bio; }
    public List<String> getHobbies() { return hobbies; }
    public String getAvatar() { return avatar; }
}

Использование Builder

// Создание профиля с минимальными параметрами
UserProfile profile1 = new UserProfile.Builder("John", "Doe", "john@example.com")
    .build();

// Создание полного профиля - читаемо и удобно!
UserProfile profile2 = new UserProfile.Builder("Jane", "Smith", "jane@example.com")
    .phone("+1234567890")
    .address("123 Main St, New York")
    .birthDate(LocalDate.of(1990, 5, 15))
    .newsletter(true)
    .bio("Passionate software engineer")
    .addHobby("Photography")
    .addHobby("Hiking")
    .avatar("jane_smith.jpg")
    .build();

// Порядок методов не важен
UserProfile profile3 = new UserProfile.Builder("Bob", "Johnson", "bob@example.com")
    .avatar("bob.jpg")
    .address("456 Oak Ave")
    .phone("+9876543210")
    .bio("Full-stack developer")
    .build();

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

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

// ✗ Без Builder - какой параметр за что отвечает?
UserProfile user = new UserProfile("John", "Doe", "john@example.com", "+1234567890", "123 St", null, true, "Engineer", Arrays.asList("code"), "john.jpg");

// ✓ С Builder - все понятно
UserProfile user = new UserProfile.Builder("John", "Doe", "john@example.com")
    .phone("+1234567890")
    .address("123 St")
    .newsletter(true)
    .bio("Engineer")
    .addHobby("code")
    .avatar("john.jpg")
    .build();

2. Гибкость

Легко добавлять новые параметры без изменения API:

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

// Старый код продолжает работать
UserProfile user = new UserProfile.Builder("John", "Doe", "john@example.com").build();

3. Безопасность

Обеспечивает создание правильных объектов через валидацию:

public UserProfile build() {
    // Валидация
    if (firstName == null || firstName.isEmpty()) {
        throw new IllegalArgumentException("First name cannot be empty");
    }
    if (lastName == null || lastName.isEmpty()) {
        throw new IllegalArgumentException("Last name cannot be empty");
    }
    if (email == null || !email.contains("@")) {
        throw new IllegalArgumentException("Invalid email format");
    }
    if (birthDate != null && birthDate.isAfter(LocalDate.now())) {
        throw new IllegalArgumentException("Birth date cannot be in the future");
    }
    
    return new UserProfile(this);
}

4. Immutability

Объекты становятся immutable (неизменяемые), что полезно для многопоточности:

public final class UserProfile {  // final класс
    private final String firstName;  // final поля
    // ...
    // Нет setters!
}

Практический пример: HTTP Request Builder

public class HttpRequest {
    private final String url;
    private final String method;
    private final Map<String, String> headers;
    private final String body;
    private final long timeout;
    private final boolean followRedirects;
    
    private HttpRequest(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers;
        this.body = builder.body;
        this.timeout = builder.timeout;
        this.followRedirects = builder.followRedirects;
    }
    
    public static class Builder {
        private final String url;  // Обязательный
        private String method = "GET";
        private Map<String, String> headers = new HashMap<>();
        private String body = null;
        private long timeout = 30000;  // мс
        private boolean followRedirects = true;
        
        public Builder(String url) {
            this.url = url;
        }
        
        public Builder method(String method) {
            this.method = method;
            return this;
        }
        
        public Builder header(String key, String value) {
            headers.put(key, value);
            return this;
        }
        
        public Builder body(String body) {
            this.body = body;
            return this;
        }
        
        public Builder timeout(long timeoutMs) {
            this.timeout = timeoutMs;
            return this;
        }
        
        public Builder followRedirects(boolean follow) {
            this.followRedirects = follow;
            return this;
        }
        
        public HttpRequest build() {
            return new HttpRequest(this);
        }
    }
}

// Использование
HttpRequest request = new HttpRequest.Builder("https://api.example.com/users")
    .method("POST")
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer token123")
    .body("{\"name\":\"John\"}")
    .timeout(60000)
    .followRedirects(false)
    .build();

Builder с Lombok (современный подход)

В современных проектах часто используется аннотация @Builder из Lombok:

import lombok.Builder;
import lombok.Value;
import java.time.LocalDate;
import java.util.List;

@Value
@Builder
public class UserProfile {
    String firstName;
    String lastName;
    String email;
    String phone;
    String address;
    LocalDate birthDate;
    boolean newsletter;
    String bio;
    List<String> hobbies;
    String avatar;
}

// Использование - то же самое!
UserProfile user = UserProfile.builder()
    .firstName("John")
    .lastName("Doe")
    .email("john@example.com")
    .phone("+1234567890")
    .build();

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

  1. Много параметров (>3-4) - конструктор становится нечитаемым
  2. Много опциональных параметров - нет смысла писать множество конструкторов
  3. Сложная валидация - можно сделать в методе build()
  4. Immutability важна - многопоточные приложения
  5. API дизайн - красивый и интуитивный API

Недостатки Builder

  1. Больше кода - нужно писать дополнительный класс
  2. Производительность - создание двух объектов вместо одного
  3. Complexity - для простых объектов излишне

Резюме

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

Что такое шаблон Builder? | PrepBro