← Назад к вопросам
Что такое шаблон 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
- Много параметров (>3-4) - конструктор становится нечитаемым
- Много опциональных параметров - нет смысла писать множество конструкторов
- Сложная валидация - можно сделать в методе build()
- Immutability важна - многопоточные приложения
- API дизайн - красивый и интуитивный API
Недостатки Builder
- Больше кода - нужно писать дополнительный класс
- Производительность - создание двух объектов вместо одного
- Complexity - для простых объектов излишне
Резюме
Builder - это мощный паттерн для создания сложных объектов. Он делает код читаемым, безопасным и гибким. Используй его когда объект имеет несколько параметров, особенно опциональных.