Обзор паттернов проектирования: Singleton, Factory, Observer
В этой статье я расскажу тебе про несколько самых полезных паттернов на Python. Готов? Поехали!
Паттерн Singleton
Первый паттерн, который стоит выучить - Singleton. Он нужен, когда в программе должен быть только один экземпляр какого-то класса. Например, класс настроек приложения.
Singleton гарантирует, что у тебя будет только один объект этого класса. Вот пример кода:
class Settings:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceЗдесь мы создали класс Settings и переопределили метод __new__. Он вызывается при создании нового объекта. Мы проверяем, существует ли уже экземпляр в _instance. Если нет - создаем. Если есть - возвращаем существующий.
Таким образом, сколько бы раз мы не вызывали Settings(), у нас будет только один объект. Проверим:
s1 = Settings() s2 = Settings() print(s1 == s2) # True - объект один и тот же
Как видишь, Singleton - простой и полезный паттерн, чтобы иметь только один экземпляр класса.
Паттерн Factory
Следующий важный паттерн - Factory (Фабрика). Он нужен, когда у тебя много похожих классов, и ты хочешь упростить их создание.
Например, в игре у тебя есть классы Goblin, Skeleton, Orc - разных монстров. Чтобы не писать в коде goblin = Goblin(), skeleton = Skeleton() и т.д., можно сделать фабрику:
class MonsterFactory:
def create_monster(self, type):
if type == "goblin":
return Goblin()
elif type == "skeleton":
return Skeleton()
elif type == "orc":
return Orc()
factory = MonsterFactory()
monster = factory.create_monster("skeleton")Теперь, чтобы создать монстра, достаточно вызвать метод create_monster и указать тип. Код становится чище и проще для понимания.
Паттерн Factory - очень удобный способ создавать схожие объекты в одном месте, не засоряя код.
Паттерн Observer
Еще один полезный паттерн - Observer (Наблюдатель). Он нужен, когда объекты в программе должны реагировать на изменения в других объектах.
Представь, в игре у тебя есть юниты и здания. И юниты должны автоматически атаковать вражеские здания, когда те появляются.
Мы можем сделать это с Observer:
from __future__ import annotations
from abc import ABC, abstractmethod
from random import randint
class Observable(ABC):
def __init__(self):
self.observers = []
def notify(self):
for observer in self.observers:
observer.update(self)
def attach(self, observer):
self.observers.append(observer)
class Observer(ABC):
@abstractmethod
def update(self, subject: Observable) -> None:
pass
class Unit(Observer):
def update(self, building: Building):
print(f"Unit is attacking {building}")
building.hp -= randint(1, 10)
class Building(Observable):
def __init__(self):
super().__init__()
self.hp = 100
# Использование
building = Building()
unit = Unit()
building.attach(unit)
building.notify() # Unit attacks buildingЗдесь Building наследует от Observable и может уведомлять наблюдателей о событиях через notify(). А Unit реализует интерфейс Observer и получает уведомления в методе update().
Таким образом юниты могут автоматически реагировать на появление зданий. Это и есть Observer - мощный паттерн для реакции объектов друг на друга.
Другие паттерны
Я рассказал про самые важные паттерны. Но есть и другие полезные:
Command - помещает задачи в объекты-команды для многократного выполнения или отмены.
Strategy - позволяет легко переключать разные алгоритмы решения задачи.
Decorator - добавляет объектам новое поведение без изменения кода.
Все эти паттерны тоже пригодятся в больших проектах. Поэтому стоит разобраться с ними, когда появится время.
Итог
Ну вот мы и разобрали основные паттерны проектирования на Python. Как видишь, это очень полезные и мощные инструменты. Они помогут тебе писать правильный, гибкий и поддерживаемый код. А это ключевое умение для профессионального программиста.