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(), который работает с кнопками, но он не знает, какие это кнопки — Виндовые или Веб. Он просто вызывает абстрактный метод 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, не задумываясь, какая ОС под капотом.

Сильные стороны:

  • Гарантирует, что продукты сочетаются друг с другом (вы не создадите Виндовую кнопку внутри Мак-окна).
  • Изолирует код создания объектов от бизнес-логики.

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) — полная рекурсивная копия (медленнее, но создает полностью независимый клон).

Используйте этот паттерн, когда:

  • Инициализация объекта стоит дорого (запросы к БД).
  • У вас есть сложный объект, который вы хотите сохранить "как шаблон" и штамповать его копии, меняя только пару полей.
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ