Menu
Coddy logo textTech

Паттерн 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. Этот паттерн особенно полезен, когда у объектов много необязательных полей, что делает ваш код более читаемым и менее подверженным ошибкам, чем длинные списки параметров.

challenge icon

Задание

Легко

Давайте создадим систему заказа пиццы, используя паттерн Builder (Строитель)! Вместо того чтобы иметь дело с конструктором, который принимает десятки параметров для всех возможных начинок и опций, вы создадите гибкий строитель, который сделает настройку пиццы чистой и читаемой.

Вы организуете свой код в двух файлах:

  • Pizza.java: Создайте класс Pizza, который использует паттерн Builder для конструирования. Ваша пицца должна иметь следующие поля:

    size (String) — обязательное, представляет размер пиццы

    cheese (boolean) — необязательное, по умолчанию false

    pepperoni (boolean) — необязательное, по умолчанию false

    mushrooms (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: Собрать пиццу и вывести её описание
        
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование