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)— повна рекурсивна копія (повільніше, але створює повністю незалежний клон).
Використовуйте цей патерн, коли:
- Ініціалізація об'єкта коштує дорого (запити до БД).
- У вас є складний об'єкт, який ви хочете зберегти «як шаблон» і штампувати його копії, змінюючи лише кілька полів.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ