JavaRush /Курси /Architecture & Logic /Породжувальні патерни

Породжувальні патерни

Architecture & Logic
Рівень 1 , Лекція 2
Відкрита

Singleton

Одинак — породжувальний патерн, який гарантує, що клас має лише один екземпляр, та надає глобальну точку доступу до нього.

    classDiagram
        direction BT
        class DatabaseConnection {
            - instance: DatabaseConnection
            - connection_string: str
            + get_instance()
            + execute_query()
        }
        note for DatabaseConnection "У Python це часто реалізується
просто на рівні модуля."

У Java або C++ програмісти витрачають багато зусиль, щоб створити «чесний» Сінглтон: блокують потоки, використовують private конструктори та статичні методи getInstance().

Модуль — це Сінглтон!

У Python механізм імпорту гарантує, що код модуля виконується лише один раз. Якщо ви створите об'єкт у файлі db.py та імпортуєте його у 10 різних місцях, ви всюди отримаєте посилання на той самий об'єкт.

Приклад, з яким ви працювали: logging. Коли ви робите logging.getLogger(), ви отримуєте посилання на глобальний менеджер логів.

Плюси:

  • Економія ресурсів (одне з'єднання з БД замість ста).
  • Глобальний доступ до налаштувань або конфігурації.

Мінуси:

  • Глобальний стан — це зло для тестів. Якщо один тест змінить Сінглтон, другий тест може впасти.
  • Складніше відстежувати залежності: «хто саме змінив це налаштування?».

Factory Method

Фабричний метод — патерн, який визначає загальний інтерфейс для створення об'єктів, але дозволяє підкласам вирішувати, який саме клас створювати.

    classDiagram
        direction BT
        class Dialog {
            +render()
            +create_button() Button
        }
        class WindowsDialog {
            +create_button()
        }
        class WebDialog {
            +create_button()
        }

        class Button {
            <<Interface>>
            +on_click()
        }
        class WindowsButton {
            +on_click()
        }
        class HTMLButton {
            +on_click()
        }

        WindowsDialog --|> Dialog
        WebDialog --|> Dialog
        WindowsButton ..|> Button
        HTMLButton ..|> Button

        WindowsDialog ..> WindowsButton : Створює
        WebDialog ..> HTMLButton : Створює
    

Суть патерна: у нас є код, наприклад, Dialog.render(), який працює з кнопками, але він не знає, які це кнопки — Windows чи Web. Він просто викликає абстрактний метод create_button(), а конкретну кнопку створює підклас.

Python-way:

У Python класи — це об'єкти першого класу. Нам часто не потрібна складна ієрархія творців. Ми можемо просто передати клас як аргумент:

def create_dialog(button_cls): return button_cls()

Або використовувати @classmethod для реалізації альтернативних конструкторів. Класичний приклад зі стандартної бібліотеки: datetime.fromtimestamp() — це фабричний метод, що створює об'єкт дати з числа.

Abstract Factory

Абстрактна фабрика — це «Фабрика фабрик». Патерн, що дозволяє створювати сімейства пов'язаних об'єктів (Кнопка + Чекбокс + Скролбар), не прив'язуючись до конкретних класів.

    classDiagram
        direction LR
        class GUIFactory {
            <<Interface>>
            +create_button()
            +create_checkbox()
        }
        class WinFactory {
            +create_button()
            +create_checkbox()
        }
        class MacFactory {
            +create_button()
            +create_checkbox()
        }

        class Application {
            -factory: GUIFactory
            +paint()
        }

        Application --> GUIFactory
        WinFactory ..|> GUIFactory
        MacFactory ..|> GUIFactory
    

Припустимо, ви пишете кросплатформовий застосунок. Якщо ви запустилися на Windows, усі елементи (кнопки, вікна, меню) мають бути у стилі Windows. Якщо на MacOS — у стилі MacOS.

Приклад із життя Python: pathlib

Коли ви пишете Path('.') у Python, ви використовуєте фабрику.
Якщо ви запустите цей код на Windows, вам повернеться об'єкт WindowsPath.
Якщо на Linux — PosixPath.
Ваш код продовжує працювати з абстрактним Path, не замислюючись, яка ОС «під капотом».

Сильні сторони:

  • Гарантує, що продукти поєднуються один з одним (ви не створите Windows-кнопку всередині Mac-вікна).
  • Ізолює код створення об'єктів від бізнес-логики.

Prototype

Прототип — патерн, що дозволяє копіювати об'єкти, не вникаючи в подробиці їхньої реалізації (не залежачи від їхніх класів).

    classDiagram
        direction LR
        class Prototype {
            <<Interface>>
            +clone()
        }
        class Sheep {
            -name
            -dna_code
            +clone()
        }

        Sheep ..|> Prototype
        note for Sheep "Метод clone() створює
точну копію овечки."

У деяких мовах створення об'єкта через конструктор — дорога операція (потрібно звертатися до бази, читати файли тощо). Простіше взяти вже готовий налаштований об'єкт і клонувати його.

Python-way: Модуль copy

У Python не потрібно реалізовувати інтерфейс Cloneable. У нас є стандартний модуль copy:

  • copy.copy(obj) — поверхнева копія (швидко, але вкладені об'єкти залишаються спільними).
  • copy.deepcopy(obj) — повна рекурсивна копія (повільніше, але створює повністю незалежний клон).

Використовуйте цей патерн, коли:

  • Ініціалізація об'єкта коштує дорого (запити до БД).
  • У вас є складний об'єкт, який ви хочете зберегти «як шаблон» і штампувати його копії, змінюючи лише кілька полів.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ