Перейти до основного вмісту

Патерн Builder в Java

Проблема

У нас є клас, який містить велику кількість полів. Частина із цих полів є обов'язковими, а частина ні. Потрібно створити "інструменти" для створення екземплярів цього класу.

Приклад. У нас є клас Користувач (User), який містить 3 обов'язкових поля: Ім'я, Прізвище, ЕлектроннаПошта та ряд неов'язкових.


public class User {
    private String firstName;
    private String lastName;
    private String email;
    private String middleName;//optional
    private int age;//optional
    private String address;//optional
    private String comment;//optional
    private String phone;//optional
    //...
}

Розглянемо можливі рішення

Використання перевантажених конструкторів

В Java є можливість створити ряд конструкторів із різним набором параметрів.


public class User {
    private String firstName;
    private String lastName;
    private String email;
    private String middleName;//optional
    private int age;//optional
    private String address;//optional
    private String comment;//optional
    private String phone;//optional

    public User(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    public User(String firstName, String lastName, String email, String middleName, int age, String address, String comment, String phone) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.middleName = middleName;
        this.age = age;
        this.address = address;
        this.comment = comment;
        this.phone = phone;
    }

    public User(String firstName, String lastName, String email, String middleName) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.middleName = middleName;
    }

    public User(String firstName, String lastName, String email, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.age = age;
    }
    
    //...
}

Недоліки підходу:

  • Якщо необов'язкових полів багато, то можливих варіантів конструкторів буде дуже багато.
  • Якщо поля мають однаковий тип, то важко буде (або й неможливо) створити конструктор для кожного варіанту.
  • При виклику конструктора потрібно буде враховувати порядок параметрів. А при цьому легко помилитися, особливо, коли параметри мають однаковий тип.

Використання сеттерів

Можна, наприклад, ініціалізувати обов'язкові параметри через конструктор, а інші - через сетери:


public class User {
    private String firstName;
    private String lastName;
    private String email;
    private String middleName;//optional
    private int age;//optional
    private String address;//optional
    private String comment;//optional
    private String phone;//optional

    public User(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    //...
}

Недоліки підходу:

  • Використання сеттерів не дуже гарний стиль програмування.
  • Неможливо створити незмінні (immutable) об'єкти.

Використання патерну Будівельник

Патерн Будівельник позбавлений недоліків попередніх двох підходів.

Один із напростіших підходів реалізації цього патерна, це створення статичного вкладеного класу. Цей клас має містити такі ж поля, як і зовнішній клас. Зрозуміло, що необо'язкові поля можна ініціалізувати значеннями по замовчуванню. Вкладений клас повинен мати конструктор для обов'язкових полів. Для ініціалізації необов'язкових полів використовуються методі, схожі на сетери, але які повертають об'єкт класу будівельника.


    public static class Builder{
        private String firstName;
        private String lastName;
        private String email;
        private String middleName="";
        private int age=25;
        private String address="";
        private String comment="No comment";
        private String phone="+555555555";

        public Builder(String firstName, String lastName, String email) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.email = email;
        }

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

        public Builder age(int age) {
            this.age = age;
            return this;
        }
        
        //...


    }

В класі User потрібно створити приватний конструктор, який в якості параметра приймає екземпляр класу Builder:


    private User(Builder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.email = builder.email;
        this.middleName = builder.middleName;
        this.age = builder.age;
        this.address = builder.address;
        this.comment = builder.comment;
        this.phone = builder.phone;
    }

В класі Builder сторити метод, що викликає цей конструктор:


        public User build(){
            return new User(this);
        }

Повний код класу User:


public class User {
    private String firstName;
    private String lastName;
    private String email;
    private String middleName;//optional
    private int age;//optional
    private String address;//optional
    private String comment;//optional
    private String phone;//optional

    private User(Builder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.email = builder.email;
        this.middleName = builder.middleName;
        this.age = builder.age;
        this.address = builder.address;
        this.comment = builder.comment;
        this.phone = builder.phone;
    }

    public static class Builder{
        private String firstName;
        private String lastName;
        private String email;
        private String middleName="";
        private int age=25;
        private String address="";
        private String comment="No comment";
        private String phone="+555555555";

        public Builder(String firstName, String lastName, String email) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.email = email;
        }

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

        public Builder age(int age) {
            this.age = age;
            return this;
        }

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

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

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

        public User build(){
            return new User(this);
        }
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getEmail() {
        return email;
    }

    public String getMiddleName() {
        return middleName;
    }

    public int getAge() {
        return age;
    }

    public String getAddress() {
        return address;
    }

    public String getComment() {
        return comment;
    }

    public String getPhone() {
        return phone;
    }

    //...

}

Сворення об'єкту з допомогою будівельника:


User user=new User.Builder("Петро","Іваненко","someemail@mail.com")
        .age(35)
        .build();

Коментарі

Популярні публікації

Колекції в Elixir

Списки Що таке список у Elixir У Elixir список — це впорядкована колекція елементів, реалізована як однозв’язний список . Це означає, що кожен елемент (вузол) зберігає посилання на наступний, але не на попередній. Така структура дозволяє швидко додавати елементи на початок списку, але повільно доступати до довільного елементу. У Java подібну структуру представляє LinkedList — частина Java Collections Framework. Вона реалізована як двозв’язний список, що забезпечує зручне додавання/видалення елементів з початку або кінця списку. Створення списків У Elixir список створюється за допомогою квадратних дужок: list = [1, 2, 3, 4] Додавання елементів У Elixir новий елемент можна додати тільки на початок списку за допомогою оператора | : # Elixir list = [2, 3, 4] new_list = [1 | list] # [1, 2, 3, 4] Доступ до елементів У Elixir немає прямого доступу до елементів за індексом, але це можна зробити через Enum.at : Enum.at([10, 20, 30], 1) # 20 Ітерація по списку У Elixir ...

Інструменти для роботи з Node.js

Що таке npm? npm (Node Package Manager) — це офіційний пакетний менеджер для Node.js . Він дозволяє: Встановлювати сторонні бібліотеки та фреймворки Керувати залежностями проєкту Запускати скрипти (команди) через package.json Приклад ініціалізації проєкту з npm npm init -y Файл package.json (скорочений приклад) { "name": "my-project", "version": "1.0.0", "scripts": { "start": "ts-node src/index.ts", "build": "tsc" }, "dependencies": { "express": "^4.18.0" }, "devDependencies": { "typescript": "^5.0.0", "ts-node": "^10.0.0" } } Що таке tsconfig.json? tsconfig.json — це файл конфігурації для компілятора TypeScript, який визначає, як слід компілювати код. Приклад файлу tsconfig.json { "compilerOptions": { "target": "ES2020...

Атоми в мові програмування Elixir

Атоми в Elixir Атоми є фундаментальною концепцією в Elixir , що відіграє ключову роль у створенні надійних та масштабованих систем. В Elixir це специфічний тип даних, який є константою , незмінною , ідентифікованою за своїм ім'ям . Отже, атом в Elixir — це іменована константа, що представляє себе. Уявіть, що ви даєте унікальне ім'я певній речі, і це ім'я завжди посилається саме на цю річ, і ніколи на щось інше. Наприклад, атом :ok завжди буде означати саме успішне завершення операції, а не якесь інше значення. Технічно, атоми є похідними від чисел . Кожен унікальний атом зберігається у таблиці атомів, і йому присвоюється унікальний цілочисельний ідентифікатор. Це робить їх надзвичайно ефективними для порівняння: замість порівняння рядків (що є повільною операцією), Elixir порівнює цілочисельні ідентифікатори. Переваги та особливості використання атомів Переваги атомів: Ефективність. Завдяки своєму числовому представленню, порівняння атомів є дуже швидким. Це осо...

Встановлення PostgreSQL на Ubuntu-сервер

Встановлення Оновлюємо пакети та встановлюємо PostgreSQL: sudo apt update sudo apt install -y postgresql postgresql-contrib Перевіряємо статус сервісу: sudo systemctl status postgresql Якщо PostgreSQL не запущений, запустимо його: sudo systemctl start postgresql sudo systemctl enable postgresql Налаштування безпеки Зміна пароля: sudo -u postgres psql У консолі PostgreSQL: ALTER USER postgres PASSWORD 'міцний_пароль'; \q \q - вихід з консолі. Список основних команд для роботи з PostgreSQL можна переглянути за посиланням. За замовчуванням PostgreSQL слухає localhost (127.0.0.1). Щоб дозволити доступ із зовнішніх машин, редагуємо конфігурацію: sudo nano /etc/postgresql/17/main/postgresql.conf (замість 17 вкажи версію PostgreSQL, яку встановлено) Шукаємо рядок: #listen_addresses = 'localhost' та замінюємо на listen_addresses = '*' Зберігаємо (Ctrl + X, Y, Enter). Тепер редагуємо pg_hba.conf: sudo nano /etc/postgresql/17/main/pg_hba.conf...

Основи Node.js

Що таке Node.js? Node.js — це середовище виконання JavaScript поза браузером, побудоване на рушії Google V8 . Воно дозволяє запускати JavaScript на сервері, створюючи серверні застосунки з високою продуктивністю. Основні характеристики: Однопотокова модель з неблокуючим I/O Асинхронне виконання за допомогою event loop Висока продуктивність у роботі з мережевими запитами npm — найбільший реєстр пакетів Що таке event loop? Event loop — це механізм в Node.js, який дозволяє неблокуючим асинхронним операціям виконуватись у середовищі з єдиним потоком. Він постійно перевіряє наявність подій у черзі та викликає відповідні колбеки. Як працює однопоточність у Node.js? Node.js використовує один потік (main thread) для обробки JavaScript-коду. Операції, які займають час (мережеві запити, читання з файлової системи), делегуються до системних API або thread pool, і після завершення результат повертається у основний потік через event loop. Приклад: асинхронна о...