Автор оригинала: Michael Petersen.
Мастер-кодеры ведут себя как архитекторы, которые соединяются и строят на разных Дизайн шаблонов создать функциональное целое. Один из самых важных шаблонов дизайна – это Singleton – класс, который имеет только один экземпляр. Вы можете спросить: Как это выглядит? Давайте посмотрим на код, реализующий синглтон в нашем интерактивном коде, оболочке:
Упражнение : Попробуйте создать несколько экземпляров класса Singleton. Ты можешь сделать это?
Давайте погрузимся в более глубокое понимание синглтона. Мы обсудим этот код в нашем первом методе, поэтому продолжайте читать!
Что такое синглтон?
Singleton – это класс, который имеет только один экземпляр. Все переменные для класса указывают на тот же экземпляр. Это просто и просто создать и использовать, и это один из шаблонов дизайна, описанных Банда из четырех Отказ После создания первого экземпляра все остальные создания создаются на первом создании экземпляра. Это также решает проблему глобального доступа к ресурсу без использования глобальных переменных. Мне нравится это краткое определение от Голова Первый дизайн шаблонов :
Шаблон Singleton гарантирует, что у класса имеет только один экземпляр, и обеспечивает глобальную точку доступа к нему.
Почему вам нужен синглтон?
Если вы читаете это, вы, вероятно, уже имеете возможное использование. Singleton – одна из банды из четырех лет Творческий шаблоны. Читайте дальше, чтобы определить, является ли его хороший кандидат на проблему, вам нужно решить.
Singleton можно использовать для доступа к общему ресурсу, такому как базу данных или Файл Отказ На его использовании есть немного противоречия. На самом деле, противоречие могут быть описаны как откровенные синглтон, божемые. Если это касается вас, я перечислял некоторые из объектов ниже с некоторыми ссылками. Несмотря на то, что Singletons могут быть полезны и питон. От Дзен Питона (Pythonistas говорят ом):
- Просто лучше сложного
- Практичность побеждает чистоту
Тем не менее, возражения имеют заслуги и могут обратиться к коду, над которой вы работаете. И даже если они не применяются, понимание тех возражений может дать вам лучшее понимание объектных принципов и тестирование единиц.
Singleton может быть полезен для контроля доступа к чему-либо, что изменяется во всем мире, когда он используется. В дополнение к базам данных и файлам Singleton может обеспечить выгоду для доступа к этим ресурсам:
- Регистратор
- Нить пулы
- кэширует
- Диалоговые окна
- HTTP-клиент
- обрабатывает на настройки предпочтения
- Объекты для ведения журнала
- Ручки для драйверов устройств, таких как принтеры.
- (?) Любой единый ресурс или глобальный сбор
Singleton можно использовать вместо использования глобальной переменной. Глобальные переменные потенциально грязны. У Singletons имеют некоторые преимущества по глобальным переменным. Синглтон может быть создан с нетерпением или ленивым творением. Стремление может создать ресурс, когда запускается программа. Ленивое творение создаст экземпляр только тогда, когда он сначала нужен. Глобальные переменные будут использовать нетерпеливое создание, нравится вам это или нет. Singletons не загрязняют глобальное пространство имен.
И, наконец, синглтон может быть частью более крупного рисунка дизайна. Это может быть частью любого из следующих шаблонов:
- Абстрактный заводской шаблон
- Строитель шаблон
- узор прототипа
- фасадный рисунок
- Узор состояний объектов, если вы не слышали об этом, никаких забот. Это не повлияет на ваше понимание рисунка Singleton.
Реализация
Стандартные реализации C # и Java полагаются на создание класса с частным конструктором. Доступ к объекту предоставляется через метод: GetInstance ()
Вот типичный ленивый синглтон внедрение в Java:
public Singleton { private static Singleton theOnlyInstance; private Singleton() {} public static Singleton getInstance() { if (theOnlyInstance) == null){ theOnlyInstance = new Singleton() } return new Singleton(); } }
Есть много способов реализации Singleton в Python Отказ Я покажу все четыре первыми и обсудим их ниже.
Метод 1: использовать __new__
class Singleton: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super(Singleton, cls).__new__(cls) # Initialize here. No need to use __init__().. cls.val = 0 return cls._instance def business_method(self, val): self.val = val x = Singleton() y = Singleton() x.val = 42 x is y, y.val
Используется Python Dumper __new__
Это было добавлено к Python, чтобы обеспечить альтернативное метод создания объекта. Это тот тип использования Case __new__
был разработан для
Плюсы :
- Я считаю, что эта реализация самая близкая по душе в реализации GOF. Он будет выглядеть знакомым для кого-либо, знакомых со стандартным исполнением Singleton.
- Легко понять, что значение кода важно для команд и обслуживания.
- Использует один класс для создания и реализации Singleton.
Минусы :
Несмотря на его «правильность», многие кодеры Python должны будут посмотреть
__new__
Чтобы понять специфику создания объекта. Достаточно знать, что__new__
создает объект.- Код, который обычно идет
__init__
можно разместить в__new__
Отказ Для того, чтобы правильно работать переопределенным
__new__
должен позвонить своим родителям__new__
метод. В этом случае объект является родительским. Информация происходит здесь с этой строкой:Объект .__ Новое __ (Class_, * args, ** kwargs)
Способ 2: декоратор
def singleton(Cls): singletons = {} def getinstance(*args, **kwargs): if Cls not in singletons: singletons[Cls] = Cls(*args, **kwargs) return singletons[Cls] return getinstance @singleton class MyClass: def __init__(self): self.val = 3 x = MyClass() y = MyClass() x.val = 42 x is y, y.val, type(MyClass)
Плюс
- Код для записи декоратора отдельно от создания класса.
- Это можно повторно использовать как можно больше синглтонов, сколько вам нужно.
- Синглтон декоратор отмечает намерение, которое ясно и понятно
Господин
Звонок
Тип (MyClass)
будет разрешаться как Функция Отказ- Создание метода класса в
MyClass
приведет к ошибке синтаксиса.
- Создание метода класса в
Если вы действительно хотите использовать декоратор и должны сохранить определение класса, есть способ. Вы можете использовать эту библиотеку:
pip install singleton_decorator
Библиотека Singleton_Decorator
обертывания и переисканы класс Singleton. Поочередно вы можете написать свой собственный. Вот реализация:
def singleton(Cls): class Decorated(Cls): def __init__(self, *args, **kwargs): if hasattr(Cls, '__init__'): Cls.__init__(self, *args, **kwargs) def __repr__(self) : return Cls.__name__ + " obj" __str__ = __repr__ Decorated.__name__ = Cls.__name__ class ClassObject: def __init__(cls): cls.instance = None def __repr__(cls): return Cls.__name__ __str__ = __repr__ def __call__(cls, *args, **kwargs): if not cls.instance: cls.instance = Decorated(*args, **kwargs) return cls.instance return ClassObject() @singleton class MyClass(): pass x = MyClass() y = MyClass() x.val = 42 x is y, y.val
Вывод:
(True, 42)
Интерактивное упражнение : Запустите следующую интерактивную визуализацию памяти. Сколько экземпляров Singleton вы найдете?
Способ 3: Используйте метакласс и наследующуюся от типа и переопределения __Call__ для создания экземпляра фильтра или фильтра
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class MyClass(metaclass=Singleton): pass x = MyClass() y = MyClass() x.val=4 x is y, y.val
Вывод выглядит следующим образом:
(True, 4)
Метод 3 создает новый Пользовательские метакласс наследующуюся от типа. MyClass затем назначает Singleton в качестве метаданных:
учебный класс):
Механика класса Singleton интересна. Он создает словарь для удержания экземпляров синглтона объектов. Ключи Dict – это названия классов. В переопределении __call__
Метод, Super .__ call__
вызывается для создания экземпляра класса. Посмотреть Пользовательские метакласс Чтобы лучше понять __call__
метод.
Плюс
- Singleton код отделен. Несколько синглтонов могут быть созданы с помощью того же
Господин
- Метаклассы остаются таинственными для многих кодеров Python. Это то что тебе нужно знать:
- В этой реализации тип унаследован:
Класс Singleton (тип)
Для того, чтобы правильно работать переопределенным
__call__
должен позвонить своим родителям__call__
метод.CLS._Instances [CLS] (Singleton, CLS) .__ Call __ (* args, ** kwargs)
- В этой реализации тип унаследован:
Метод 4: Используйте базовый класс
class Singleton: _instance = None def __new__(class_, *args, **kwargs): if not isinstance(class_._instance, class_): class_._instance = object.__new__(class_, *args, **kwargs) return class_._instance class MyClass(Singleton): pass x = MyClass() y = MyClass() x.val=4 x is y, y.val
Вывод выглядит следующим образом:
(True, 4)
Плюс
- Код может быть повторно использован для создания большего количества синглтонов
- Использует знакомые инструменты. (По сравнению с декораторами, метаклассами и методом
__new__
Во всех четырех методах экземпляр создан первый раз, когда его задают. Все звонки после первого возврата первого экземпляра.
Синглтоны в резьбовой среде
Если ваш Singleton должен работать в многопоточной среде, то ваш метод Singleton должен быть сделан в потоке. Ни один из способов выше не является безопасным. Уязвимый код находится между проверкой существующего синглтона и созданием первого примера:
if cls._instance is None: cls._instance = super(Singleton, cls).__new__(cls)
Каждая реализация имеет аналогичный кусок кода. Чтобы сделать его потоком, этот код должен быть синхронизирован.
with threading.Lock(): if cls._instance is None: cls._instance = super(Singleton, cls).__new__(cls)
Это работает нормально и с замком на месте, создание синглтона становится безопасной резьбой. Теперь каждый раз, когда поток запускает код, Threading.lock ()
называется, прежде чем он проверяет существующий экземпляр.
Если производительность не проблема, это здорово, но мы можем сделать лучше. Механизм блокировки дороги, и он должен работать только в первый раз. Создание экземпляра происходит только один раз, чтобы замок должен произойти максимум одновременно. Решение состоит в том, чтобы разместить замок после проверки проверки. Затем добавьте еще одну проверку после блокировки.
import threading ... if cls._instance is None: with threading.Lock(): if cls._instance is None: cls._instance = super(Singleton, cls).__new__(cls)
И вот как использовать « Дважды проверенные блокировки ».
Безопасная резьба версия метода 1
Рассмотрим следующую модификацию метода 1:
import threading class Singleton: _instance = None def __new__(cls): if cls._instance is None: with threading.Lock(): if cls._instance is None: cls._instance = super(Singleton, cls).__new__(cls) # Initialize here. No need to use __init__().. cls.val = 0 return cls._instance def business_method(self, val): self.val = val x = Singleton() y = Singleton() x.val = 42 x is y, y.val
Вывод:
(True, 42)
Чтобы сделать его потоком, мы добавили две строки кода. Каждый метод может быть сделан резьбой аналогичным образом
Альтернативы использования Singleton
Используйте модуль как синглтон (глобальный шаблон объекта)
В Python модули являются одинокими, уникальными и глобально доступными. Глобальный шаблон объекта рекомендуется документами Python. Он просто означает создание отдельного модуля и создать свой объект в глобальном пространстве модуля. Последующие ссылки просто нужно импортировать его.
Используйте инъекцию зависимости
Как правило, это означает использование композиции для обеспечения объектов для зависимых объектов. Он может быть реализован бесчисленным способами, но в целом, поставить зависимости в конструкторов и избежать создания новых экземпляров объектов в бизнес-методах.
Проблемы с Singletons
Из всех 23 узоров в книге семенной 1994 года Дизайн шаблонов Singleton является наиболее употребленным, самым обсуждаемым и самым популярным. Это немного кролика, чтобы просеять через тысячи блогов и посты на переполнении стека, которые говорят об этом. Но ведь синглтон ненавидит, шаблон остается распространенным. Это почему? Это потому, что условия, которые предлагают его использование, очень распространены: одна база данных, один файл конфигурации, один пул резьбы …
Аргументы против его использования лучше всего утверждаются в некоторых элегантных (и старых) сообщениях блога, которые я не могу соответствовать. Но я дам резюме и ссылки для дальнейшего чтения.
Краткое резюме
Перефразированные от Brian кнопки в Почему синглтоны злые :
- Они обычно используются как глобальный экземпляр, почему так плохо? Поскольку вы скрываете зависимости вашего приложения в вашем коде вместо того, чтобы выдержать их через интерфейсы. Делать что-то глобальное, чтобы не пройти его вокруг, это код кода. (Это некоторое эффективное призывовное название. Какой бы код кода не так, это заставляет меня съедать немного и морщить нос, как я себе представляю).
- Они нарушают единственный принцип ответственности: в силу того, что они контролируют свое собственное творение и жизненный цикл.
- Они по своей сути причины тесно связаны с кодом. Это делает их подходящим в тесте довольно сложно во многих случаях.
- Они несут состояние вокруг на всю жизнь приложения. Еще один удар для тестирования, поскольку вы можете в конечном итоге с ситуацией, когда необходимо заказать тесты, что является большим нет для модульных тестов. Почему? Поскольку каждый тест на единицу должен быть независимым от другого.
Если вы используете Singletons в вашем коде?
Если вы спрашиваете себя, основываясь на блогах других народов, вы уже находитесь в кроличке. Слово «должно» не приветствуется в дизайне кода. Используйте Singletons или нет и не знайте возможных проблем. Refastor, когда есть проблемы.
Возможные проблемы, чтобы рассмотреть
Инструменты для людей, которые знают, как их использовать. Несмотря на все плохие вещи, написанные о Singletons, люди все еще используют их, потому что:
- Они наполняют необходимость лучше, чем альтернативы.
и/или
- Они не знают лучше, и они создают проблемы в своем коде, используя их.
Избегайте проблем. Не будь в группе 2.
Проблемы с Singletons вызваны, потому что они нарушают правило на одну ответственность. Они делают три вещи:
- Гарантия только один экземпляр существует
- Предоставить глобальный доступ к этому экземпляру
- Предоставить свою собственную бизнес-логику.
- Потому что они нарушают правило на одну ответственность, синглтоны могут быть трудно проверить
- Инверсия контроля МОК и Инъекция зависимости Являются ли модели, предназначенные для преодоления этой проблемы в ориентированной объектно-ориентированной форме, которая помогает сделать тестичный код.
- Singletons могут привести к крепко Советный код Отказ Глобальный экземпляр, который имеет непостоянное состояние, может потребовать от объекта, чтобы зависеть от состояния глобального объекта.
- Это директор OO для Отдельная творческая логика из бизнес-логики Отказ Придерживаясь этого принципа «Singletons Должны никогда не будешь использовать». Снова со словом следует. Вместо этого, будь Йода: « сделать или нет! ». Основание решения по вашему собственному коду.
- Память, выделенная синглтоном, не может быть освобождена. Это только проблема, которую необходимо освободить память.
- В оформлении Сбор мусора Окружающая среда Singletons может стать вопросом управления памятью.
Дальнейшее изучение
- Брэндон Родос, Синглтон шаблон
- Мишко Хревери, Singleton Я люблю тебя – но вы приносите меня Отказ Отказался от комментариев
- Мишко Хревери, Singletons являются патологическими лжецами
- Мишко Хревери, Где у всех синглтонов ушли
- Википедия Singleton_Pattern.
- Майкл Сафаян, Singleton Anti-Pattern
- Марк Радфорд Singleton, антизаблоки
- Алекс Миллер, Узоры я ненавижу # 1: Singleton
Скотта Денсмора/Брайан, Почему одиноки злые
- Мартин Брэмптон, Хорошо использованные синглтоны хороши!
- Обсуждение, отредактированное Cunningham & Cunningham, Синглтон Глобальные проблемы
- Роберт Нистрем, Дизайн шаблонов пересматривается: Singleton
- Стив Егге, Singleton считается глупо
- J.b. жгнбергер Используйте свои синглтоны с умом
Мета-ноты – Мишко Хревери.
Хевери работал в Google, когда он написал эти блоги. Его блоги были читаемыми, развлекательными, информативными, провокационными и вообще завышенными, чтобы сделать точку зрения. Если вы читаете его блоги, обязательно прочитайте комментарии. Singletons являются патологическими лжецами Имеет пример тестирования подразделения, который иллюстрирует, как Singletons могут затруднить выяснение цепей зависимости и запуск или тестирование приложения. Это довольно крайний пример злоупотребления, но он делает действительный смысл:
Singletons – не что иное, как глобальное государство. Глобальное состояние делает его, чтобы ваши объекты могли тайно получить вещи, которые не объявлены в их API, а в результате Singletons делают ваши API в патологические лжецы.
Конечно, он немного преувеличивает. Singletons обертывают глобальное состояние в классе и используются для вещей, которые являются «естественными» глобальными по природе. Как правило, hevery рекомендует инъекцию зависимостей для замены синглтонов. Это просто означает объекты, передают свои зависимости в их конструкторе.
Где у всех синглтонов ушли Делает то, что инъекция зависимостей позволила легко получить экземпляры для конструкторов, которые требуют их, что облегчает лечебную потребность позади плохих, глобальных синглтонов в патологических лжецах.
Meta Notes – Брэндон Родос Синглтон Узор
Python Programmers почти никогда не реализуют шаблон Singleton, как описано в банде из четырех книг, чья Singleton Class запрещает нормальное зрелище и вместо этого предлагает метод класса, который возвращает экземпляр Singleton. Python более элегантный, и позволяет классу продолжать поддерживать нормальный синтаксис для создания института при определении пользовательских __new __ ()
Метод, который возвращает экземпляр Singleton. Но еще более Pythonic подход, если ваш дизайн заставляет вас предлагать глобальный доступ к объекту Singleton, вместо этого используется глобальный шаблон объекта.
Meta Notes – J.B. Rainsberger Удивительно используют свои синглеты
Знать, когда использовать синглтоны, и когда оставить их позади
J.B. Rainsberger
Опубликовано 01 июля 2001 года 2001 г. Автоматизированное тестирование подразделения наиболее эффективно, когда:
- Связь между классами только так же сильна, как это должно быть
- Просто использовать MOCK Реализации сотрудничающих классов вместо реализации производства
Singletons знают слишком много
Существует одна реализация анти-образца, которая процветает в приложении с слишком многими синглетами: я знаю, где вы живете анти-образцом. Это происходит, когда, среди сотрудников классов совместных сотрудников, один класс знает, где получить экземпляры другого.
К приемлемым синглторам
Можно избежать злоупотребления синглтоном, глядя на проблему с другого угла. Предположим, что приложение необходимо только один экземпляр класса, а приложение настраивает этот класс при запуске: почему сам класс не отвечает за то, что он является синглтоном? Это кажется довольно логичным для приложения, чтобы принять эту ответственность, поскольку приложение требует такого поведения. Применение, а не компонент, должен быть синглтоном. Затем приложение делает экземпляр компонента, доступный для любого кода, специфичного приложения для использования. Когда приложение использует несколько таких компонентов, он может согревать их в то, что мы назвали инструментов.
Meta Notes – Mark Safayan Singleton Anti Pattern
Вместо того, чтобы использовать этот шаблон, просто создайте единый экземпляр и распространяйте его на места, которые используют объект в качестве параметра, чтобы сделать зависимость явной.