Frog Proger 10 октября 2024

🧩 Реализация паттерна «Одиночка» на Python

Мечтаешь о коде, который работает как швейцарские часы? Паттерн «Одиночка» может стать тем самым механизмом, который заставит все шестеренки крутиться идеально.
🧩 Реализация паттерна «Одиночка» на Python
Этот материал взят из нашей еженедельной email-рассылки, посвященной бэкенду. Подпишитесь, чтобы быть в числе первых, кто получит дайджест.

Шаблон «Одиночка» (Singleton) используется для того, чтобы гарантировать, что у класса будет только один экземпляр: когда создается новый экземпляр, возвращается уже существующий экземпляр, а не создается новый. Это важно для работы с сервисами, которые должны быть общими для всего приложения: базой данных, конфигурационными объектами или системой логирования, где создание нескольких экземпляров может привести к неэффективности или ошибкам.

При реализации паттерна «Одиночка» на Python удобно использовать декоратор – это функция, которая модифицирует или расширяет поведение другой функции или класса. Декоратор позволяет «обернуть» функцию или класс и добавить дополнительную функциональность, не изменяя их структуру.

Разберем код для реализации «Одиночки» с помощью декоратора:

Класс _SingletonWrapper. Этот класс обeртывает оригинальный класс, который мы хотим сделать одиночкой. В конструкторе __init__ мы передаем класс, сохраняем его в переменную __wrapped__ и инициализируем атрибут _instance как None. Этот атрибут будет хранить единственный экземпляр класса:

        class _SingletonWrapper:
    """
    Класс-обeртка для реализации паттерна Одиночка.
    """
    def __init__(self, cls):
        self.__wrapped__ = cls  # Оригинальный класс
        self._instance = None   # Здесь будет храниться экземпляр класса
    

Метод __call__ позволяет экземпляру класса вести себя как функции, то есть быть вызываемым. Когда вызывается объект, он проверяет, существует ли уже экземпляр класса:

  • Если _instance равно None (экземпляр еще не создан), создается новый экземпляр.
  • Если экземпляр уже создан, он просто возвращается. Таким образом мы гарантируем, что класс будет иметь только один экземпляр.
            def __call__(self, *args, **kwargs):
        """Возвращает единственный экземпляр класса"""
        if self._instance is None:
            self._instance = self.__wrapped__(*args, **kwargs)
        return self._instance
    

Декоратор singleton оборачивает класс в экземпляр _SingletonWrapper. Теперь при создании экземпляра класса будет использоваться уже существующий объект, если он был создан ранее:

        def singleton(cls):
    """
    Декоратор для класса, реализующий синглтон.
    """
    return _SingletonWrapper(cls)
    

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

Если мы обернем класс Logger декоратором @singleton:

        @singleton
class Logger:
    def __init__(self):
        self.log = []

    def write_log(self, message):
        self.log.append(message)

    def read_log(self):
        return self.log
    

То каждый раз, когда мы попытаемся создать его новый экземпляр, будет возвращен уже существующий:

        logger1 = Logger()
logger2 = Logger()
logger1.write_log("Сообщение 1")
print(logger2.read_log())  # Выведет: ['Сообщение 1']
print(logger1 is logger2)  # Выведет: True
    

Как видно, оба объекта logger1 и logger2 – это на самом деле один и тот же экземпляр.

Преимущества использования «Одиночки»

«Одиночка» – полезный паттерн при правильном применении. Его главные плюсы:

  • Глобальный доступ – позволяет централизованно управлять ресурсами, такими как подключение к базе данных или логирование.
  • Эффективность – повторное использование одного экземпляра может снизить расход памяти и повысить производительность, особенно для ресурсоемких объектов.
  • Удобство тестирования – через атрибут __wrapped__ можно получить доступ к оригинальному классу для тестирования, не затрагивая поведение.

Когда «Одиночка» может стать источником проблем

Перед реализацией шаблона следует учитывать контекст – неуместное использование «Одиночки» чревато проблемами:

  • Чрезмерное использование паттерна может привести к сильной зависимости между компонентами системы, усложняя тестирование и сопровождение кода.
  • В многопоточных приложениях глобальное состояние, которое создает шаблон, может стать источником ошибок.
  • В масштабируемых приложениях глобальное состояние тоже может вызывать сложности, особенно при распределенных вычислениях.

А ты уже использовал паттерн «Одиночка» в своих проектах? Поделись, в каких ситуациях он тебе пригодился!

***

Хочешь освоить Python и научиться применять паттерны вроде «Одиночки»? Курс «Основы программирования на Python» от Proglib Academy предлагает:

  • Пошаговое изучение от простого к сложному
  • Практику написания реальных проектов
  • Поддержку опытных менторов

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ