Какие основные паттерны проектирования часто используются в Python?

Ответ

В Python-разработке активно применяются различные паттерны проектирования для создания гибкого, масштабируемого и поддерживаемого кода. Ниже представлены некоторые из наиболее часто используемых:

  1. Singleton (Одиночка)

    • Назначение: Гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему.
    • Применение: Используется для объектов, которые должны быть уникальными в системе, например, менеджеры конфигурации, пулы соединений или логирование.

      
      class Singleton:
      _instance = None
      
      def __new__(cls):
          if not cls._instance:
              cls._instance = super().__new__(cls)
          return cls._instance

    Пример использования:

    s1 = Singleton() s2 = Singleton() print(s1 is s2) # Выведет True

  2. Factory Method (Фабричный метод)

    • Назначение: Определяет интерфейс для создания объекта, но позволяет подклассам решать, какой класс инстанцировать. Делегирует создание объектов подклассам.
    • Применение: Используется, когда класс не может предвидеть класс объектов, которые ему нужно создать, или когда подклассы должны определять, какие объекты создавать.
      
      from abc import ABC, abstractmethod

    class Product(ABC): @abstractmethod def operation(self): pass

    class ConcreteProduct(Product): def operation(self): return "ConcreteProduct operation"

    class Creator(ABC): @abstractmethod def factory_method(self) -> Product: pass

    class ConcreteCreator(Creator): def factory_method(self) -> Product: return ConcreteProduct()

    Пример использования:

    creator = ConcreteCreator() product = creator.factory_method() print(product.operation())

  3. Observer (Наблюдатель)

    • Назначение: Определяет зависимость "один-ко-многим" между объектами так, что при изменении состояния одного объекта все зависящие от него оповещаются и автоматически обновляются.
    • Применение: Используется для реализации распределенных систем событий, где изменения в одном объекте должны вызывать действия в других, не связывая их напрямую.

      
      class Subject:
      def __init__(self):
          self._observers = []
      
      def attach(self, observer):
          if observer not in self._observers:
              self._observers.append(observer)
      
      def detach(self, observer):
          self._observers.remove(observer)
      
      def notify(self, message):
          for observer in self._observers:
              observer.update(message)

    class Observer: def init(self, name): self.name = name

    def update(self, message):
        print(f"Observer {self.name} received: {message}")

    Пример использования:

    subject = Subject() observer1 = Observer("One") observer2 = Observer("Two")

    subject.attach(observer1) subject.attach(observer2)

    subject.notify("State changed!")

  4. Decorator (Декоратор)

    • Назначение: Динамически добавляет новую функциональность объекту, не изменяя его структуру. В Python реализуется с помощью функций-декораторов или классов.
    • Применение: Идеален для добавления логирования, кэширования, проверки прав доступа, измерения времени выполнения и других сквозных функций.
      
      def logger(func):
      def wrapper(*args, **kwargs):
          print(f"[LOG] Calling function: {func.__name__} with args: {args}, kwargs: {kwargs}")
          result = func(*args, **kwargs)
          print(f"[LOG] Function {func.__name__} returned: {result}")
          return result
      return wrapper

    @logger def add(a, b): return a + b

    Пример использования:

    print(add(2, 3))

Помимо перечисленных, в Python также часто используются другие паттерны, такие как Adapter (Адаптер), Strategy (Стратегия), Facade (Фасад), Iterator (Итератор) и Template Method (Шаблонный метод).

Ответ 18+ 🔞

А, паттерны проектирования! Ну это ж, блядь, как священные скрижали для любого питониста, который хочет выглядеть умно на совещании. Сидишь такой, киваешь: "Да-да, тут у нас фабрика, там наблюдатель, а вот тут, сука, декоратор прилепил — красота!" А сам думаешь: "Главное — не перепутать, какой из них за что отвечает, а то опозоришься, как полупидор".

Ну ладно, поехали разбирать эту матчасть, чтоб тебя не разоблачили, как хитрожопого самозванца.

1. Singleton (Одиночка)

Назначение: Чтобы какой-нибудь наглый объект не плодился, как кролики, по всей твоей программе. Гарантирует, что экземпляр класса будет один, и все будут тыкать в него пальцем, как в глобальную переменную, но при этом прилично. Где впихнуть: Ну, например, когда у тебя конфиг один на всю систему, а не десять разных, которые между собой дерутся, как пизда с ушами. Или логгер, чтобы он не писал в десять файлов одновременно, создавая пиздец и хаос.

class Singleton:
    _instance = None

    def __new__(cls):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance

# Пример использования:
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # Выведет True — это один и тот же ушлёпок, а не два разных!

2. Factory Method (Фабричный метод)

Назначение: Это когда ты не хочешь пачкать руки, создавая объекты напрямую через new. Говоришь какому-то другому классу: "Слушай, дружок, роди мне тут штуку, а я сам не буду разбираться, как её правильно собирать". А он тебе: "Да не вопрос, ща нафабрикаю!" Где впихнуть: Когда у тебя куча похожих объектов, но с разными настройками, и ты не хочешь в основном коде разводить эту грязь с if-elif-else на три экрана.

from abc import ABC, abstractmethod

class Product(ABC):
    @abstractmethod
    def operation(self):
        pass

class ConcreteProduct(Product):
    def operation(self):
        return "ConcreteProduct operation"

class Creator(ABC):
    @abstractmethod
    def factory_method(self) -> Product:
        pass

class ConcreteCreator(Creator):
    def factory_method(self) -> Product:
        return ConcreteProduct()

# Пример использования:
creator = ConcreteCreator()
product = creator.factory_method()
print(product.operation()) # А вот и твой свежеиспечённый продукт, получай!

3. Observer (Наблюдатель)

Назначение: Представь, что у тебя есть один важный чувак (Subject), а вокруг него куча подписчиков (Observers), которые ждут, не дождутся, когда он чихнёт, чтобы сразу побежать за носовым платком. Как только он меняется — все они тут как тут. Где впихнуть: Идеально для всяких систем событий. Например, пользователь нажал кнопку — и сразу десять разных модулей про это узнали и что-то сделали. Без этой хуйни пришлось бы всё вручную дергать, а тут — красота, один раз оповестил, и все в курсе.

class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        if observer not in self._observers:
            self._observers.append(observer)

    def detach(self, observer):
        self._observers.remove(observer)

    def notify(self, message):
        for observer in self._observers:
            observer.update(message)

class Observer:
    def __init__(self, name):
        self.name = name

    def update(self, message):
        print(f"Observer {self.name} received: {message}")

# Пример использования:
subject = Subject()
observer1 = Observer("One")
observer2 = Observer("Two")

subject.attach(observer1)
subject.attach(observer2)

subject.notify("State changed!") # И оба наблюдателя тут же охуели от новости.

4. Decorator (Декоратор)

Назначение: Это как наклейки на ноутбук. Хочешь добавить функциональность — прилепил декоратор. Не понравилось — отклеил. А сам ноутбук (функция) даже не в курсе, что его разукрасили. Где впихнуть: О, это любовь всех питонистов! Захотел залогировать вызов функции — обернул в @logger. Захотел закэшировать результат — @lru_cache из коробки. Всё, блядь, чисто и красиво, без копипасты одного и того же кода в каждую функцию.

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"[LOG] Calling function: {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"[LOG] Function {func.__name__} returned: {result}")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

# Пример использования:
print(add(2, 3))
# Выведет кучу логирования, а потом результат. Удобно же, ёпта!

Ну и конечно, есть ещё овердохуища других паттернов: Адаптер (чтобы старый кривой код притворился новым), Стратегия (меняешь алгоритм на лету, как перчатки), Фасад (прячешь за красивой дверью трёхэтажный пиздец) и так далее.

Главное — не пытайся впихнуть их все в один проект, а то получится не архитектура, а ёперный театр, где каждый класс думает, что он главный. Используй с умом, и будет тебе счастье.