Паттерн Builder
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 67 из 87.
Паттерн Строитель (Builder) — это порождающий паттерн проектирования, который решает распространенную проблему: создание сложных объектов с множеством необязательных параметров. Вместо создания множества конструкторов или передачи null для неиспользуемых параметров, Строитель позволяет конструировать объекты пошагово.
Представьте создание объекта User с обязательными полями (name, email) и необязательными (age, phone, address). Без Builder вам бы понадобились телескопические конструкторы или громоздкий конструктор с множеством параметров. Паттерн Builder предлагает более чистый подход:
public class User {
private final String name; // обязательно
private final String email; // обязательно
private final int age; // необязательно
private final String phone; // необязательно
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
}
public static class UserBuilder {
private final String name;
private final String email;
private int age;
private String phone;
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
}Методы Builder возвращают this, что позволяет использовать цепочку вызовов методов — гибкий и читаемый способ создания объектов:
User user = new User.UserBuilder("Alice", "alice@email.com")
.age(25)
.phone("555-1234")
.build();Обратите внимание, что конструктор User является приватным — объекты могут быть созданы только через Builder. Этот паттерн особенно полезен, когда у объектов много необязательных полей, что делает ваш код более читаемым и менее подверженным ошибкам, чем длинные списки параметров.
Задание
ЛегкоДавайте создадим систему заказа пиццы, используя паттерн Builder (Строитель)! Вместо того чтобы иметь дело с конструктором, который принимает десятки параметров для всех возможных начинок и опций, вы создадите гибкий строитель, который сделает настройку пиццы чистой и читаемой.
Вы организуете свой код в двух файлах:
Pizza.java: Создайте классPizza, который использует паттерн Builder для конструирования. Ваша пицца должна иметь следующие поля:size(String) — обязательное, представляет размер пиццыcheese(boolean) — необязательное, по умолчанию falsepepperoni(boolean) — необязательное, по умолчанию falsemushrooms(boolean) — необязательное, по умолчанию falseКонструктор
Pizzaдолжен быть приватным и принимать в качестве параметра толькоPizzaBuilder, копируя все значения из строителя в поля пиццы.Добавьте метод
getDescription(), который возвращает строку в следующем формате:[size] Pizza with: [toppings], где toppings — это разделенный запятыми список начинок, значения которых равны true (Cheese, Pepperoni, Mushrooms). Если начинки не выбраны, часть с начинками должна содержатьno toppings.Создайте статический вложенный класс
PizzaBuilderс:- Конструктором, который принимает обязательный параметр
size - Методами
cheese(),pepperoni()иmushrooms(), каждый из которых устанавливает соответствующее логическое значение в true и возвращаетthisдля цепочки вызовов - Методом
build(), который возвращает новый объектPizza
- Конструктором, который принимает обязательный параметр
Main.java: Продемонстрируйте работу вашего строителя пиццы! Вы получите четыре входных значения: размер пиццы (String) и три значения yes/no (Strings), указывающие, нужно ли добавить сыр, пепперони и грибы соответственно.Создайте
PizzaBuilderс заданным размером. Затем, основываясь на каждом вводе yes/no, вызовите по цепочке соответствующие методы для начинок. Если ввод равен"yes", вызовите этот метод начинки; если"no", пропустите его. Наконец, вызовитеbuild(), чтобы создать пиццу.Выведите результат вызова
getDescription()для вашей готовой пиццы.
Вы получите четыре входных значения по порядку: размер (String), добавить сыр (String: "yes" или "no"), добавить пепперони (String: "yes" или "no"), добавить грибы (String: "yes" или "no").
Например, при входных данных Large, yes, no, yes, вашим выводом будет: Large Pizza with: Cheese, Mushrooms
Обратите внимание, как паттерн Builder позволяет создавать пиццы с любой комбинацией начинок без необходимости создания отдельных конструкторов для каждого варианта!
Шпаргалка
Паттерн Строитель (Builder) — это порождающий паттерн проектирования для создания сложных объектов с множеством необязательных параметров. Он позволяет избежать «телескопических» конструкторов и длинных списков параметров, создавая объекты пошагово.
Структура
Паттерн состоит из:
- Класса с приватным конструктором, который принимает только строителя
- Статического внутреннего класса Builder, который конструирует объект
- Методов строителя, которые возвращают
thisдля цепочки вызовов (method chaining) - Метода
build(), который создает финальный объект
Пример реализации
public class User {
private final String name; // обязательно
private final String email; // обязательно
private final int age; // необязательно
private final String phone; // необязательно
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
}
public static class UserBuilder {
private final String name;
private final String email;
private int age;
private String phone;
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
}Использование с цепочкой вызовов
User user = new User.UserBuilder("Alice", "alice@email.com")
.age(25)
.phone("555-1234")
.build();Паттерн Строитель идеально подходит в тех случаях, когда объекты имеют много необязательных полей, обеспечивая читаемость и удобство поддержки кода по сравнению с конструкторами с многочисленными параметрами.
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Считать входные данные
String size = scanner.nextLine();
String addCheese = scanner.nextLine();
String addPepperoni = scanner.nextLine();
String addMushrooms = scanner.nextLine();
// TODO: Создать PizzaBuilder с заданным size
// TODO: Вызвать цепочку методов для добавок на основе входных данных yes/no
// TODO: Собрать пиццу и вывести её описание
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»