Синглтоны: хороший дизайн или костыль?

Синглтоны - это горячо обсуждаемый шаблон проектирования, поэтому мне интересно, что думает о них сообщество Stack Overflow.

Пожалуйста, укажите причины своего мнения, а не просто «Синглтоны для ленивых программистов!»

Вот довольно хорошая статья по этому поводу, хотя она против использования синглтонов: Scientificninja.com: performance-singletons .

У кого-нибудь есть еще какие-нибудь хорошие статьи о них? Может, в поддержку Синглтонов?

Ответов (23)

Священные войны! Хорошо, позвольте мне посмотреть .. В прошлый раз, когда я проверял дизайн, полиция сказала ..

Синглтоны плохи, потому что они препятствуют автоматическому тестированию - экземпляры не могут быть созданы заново для каждого тестового примера. Вместо этого логика должна быть в классе (A), который можно легко создать и протестировать. Другой класс (B) должен отвечать за ограничение создания. Принцип единой ответственности на первый план! Должно быть командным знанием, что вы должны пройти через B, чтобы получить доступ к A - своего рода командное соглашение.

Я в основном согласен ..

Цель синглтона - гарантировать, что у класса есть только один экземпляр, и предоставить к нему глобальную точку доступа. Большую часть времени основное внимание уделяется одной точке экземпляра. Представьте, если бы его назвали Глобалтоном. Это звучало бы менее привлекательно, поскольку это подчеркивает (обычно) отрицательные коннотации глобальной переменной.

Большинство веских аргументов против синглтонов связаны с трудностями, которые они представляют при тестировании, поскольку создать для них двойные тесты непросто.

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

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

У Google есть детектор синглтонов для Java, который, как мне кажется, начинался как инструмент, который необходимо запускать для всего кода, созданного в Google. Краткая причина удаления синглтонов:

потому что они могут затруднить тестирование и скрыть проблемы с вашим дизайном

Для более подробного объяснения см. « Почему синглтоны вызывают споры » от Google.

Я пытался придумать, как прийти сюда на помощь бедному сингелтону, но должен признать, что это сложно. Я видел очень мало их законного использования, и с текущим приводом для внедрения зависимостей и модульного тестирования их просто сложно использовать. Они определенно являются проявлением "культа груза" программирования с использованием шаблонов проектирования. Я работал со многими программистами, которые никогда не взламывали книгу "GoF", но они знают Singelton и, следовательно, они знают "Patterns".

Я действительно должен не согласиться с Орионом, большую часть времени я видел, как одиночки злоупотребляли не глобальными переменными в платье, а скорее глобальными услугами (методами) в платье. Интересно отметить, что если вы попытаетесь использовать Singeltons в SQL Server 2005 в безопасном режиме через интерфейс CLR, система отметит код. Проблема в том, что у вас есть постоянные данные за пределами любой данной транзакции, которая может выполняться, конечно, если вы сделаете переменную экземпляра доступной только для чтения, вы можете обойти проблему.

Этот вопрос заставил меня много переделывать за один год.

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

Их можно использовать очень редко, и обычно есть способ лучше. Я сталкивался с ситуациями, когда я что-то делал с синглтоном, а затем мне приходилось просеивать свой код, чтобы удалить его после того, как я обнаружил, насколько хуже он сделал вещи (или после того, как я придумал гораздо лучшее, более разумное решение )

Синглтон - это просто набор глобальных переменных в маскарадном костюме.

У глобальных переменных есть свое применение, как и у синглтонов, но если вы думаете, что делаете что-то крутое и полезное с синглтоном, а не с дурацкой глобальной переменной (все знают, что глобальные переменные - это плохо, ммкей), вы, к сожалению, заблуждаетесь.

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

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

Затем я создал одноэлементный класс с именем company , который инкапсулировал все о месте и был полностью заполнен данными к моменту открытия системы.

Это не было просто набором переменных в маскарадном костюме, потому что у него были десятки обязанностей, таких как взаимодействие с уровнем персистентности для сохранения / извлечения данных о компании, работа с сотрудниками и сборы цен и т. Д.

Кроме того, это была фиксированная общесистемная, легкодоступная точка для хранения данных компании.

Это не было просто набором переменных в маскарадном костюме, потому что у него были десятки обязанностей, таких как взаимодействие с уровнем персистентности для сохранения / извлечения данных о компании, работа с сотрудниками и сборы цен и т. Д.

Я должен сказать, что вы на самом деле не описываете что-то, что должно быть единым объектом, и спорно, что любой из них, кроме сериализации данных, должен был быть одиночным.

Я вижу по крайней мере 3 набора классов, в которых я обычно проектирую, но я предпочитаю более простые объекты меньшего размера, которые очень хорошо выполняют узкий набор задач. Я знаю, что это не характерно для большинства программистов. (Да, я работаю над чудовищами класса 5000 строк каждый день, и мне особенно нравятся методы на 1200 строк, которые пишут некоторые люди.)

Я думаю, дело в том, что в большинстве случаев синглтон вам не нужен, а часто вы просто усложняете себе жизнь.

Я думаю, что существует большое недоразумение по поводу использования паттерна Синглтон. Большинство комментариев здесь относятся к нему как к месту доступа к глобальным данным. Здесь нужно быть осторожным - синглтон как шаблон не предназначен для доступа к глобальным объектам.

Синглтон следует использовать, чтобы иметь только один экземпляр данного класса. В репозитории шаблонов есть отличная информация о синглтоне .

Я много раз использовал синглтоны вместе со Spring и не считал это костылем или ленивым.

Этот шаблон позволил мне создать один класс для набора значений типа конфигурации, а затем поделиться одним (неизменяемым) экземпляром этого конкретного экземпляра конфигурации между несколькими пользователями моего веб-приложения.

В моем случае синглтон содержал критерии конфигурации клиента - расположение файла css, критерии подключения к базе данных, наборы функций и т. Д. - специфичные для этого клиента. Эти классы были созданы и доступны через Spring и совместно используются пользователями с одинаковой конфигурацией (т.е. 2 пользователя из одной компании). * ** Я знаю, что у этого типа приложений есть название, но оно ускользает от меня *

Я считаю, что было бы расточительно создавать (а затем собирать мусор) новые экземпляры этих «постоянных» объектов для каждого пользователя приложения.

Синглтоны очень полезны, и их использование само по себе не является антипаттерном. Однако они заработали плохую репутацию в основном потому, что заставляют любой потребляющий код признавать, что они синглтон, чтобы взаимодействовать с ними. Это означает, что если вам когда-нибудь понадобится их «отменить синглтонизацию», влияние на вашу кодовую базу может быть очень значительным.

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

Еще лучше использовать инверсию контейнера управления! Большинство из них позволяют отделить поведение создания экземпляров от реализации ваших классов.

В защиту одиночек:

  • Они не так плохи, как глобальные, потому что глобальные переменные не имеют стандартного порядка инициализации, и вы можете легко увидеть недетерминированные ошибки из-за наивных или неожиданных порядков зависимостей. Синглтоны (при условии, что они размещены в куче) создаются после всех глобальных переменных и в очень предсказуемом месте кода.
  • Они очень полезны для систем с ленивыми ресурсами / кэширования, таких как интерфейс для медленных устройств ввода-вывода. Если вы разумно создадите одноэлементный интерфейс для медленного устройства, и никто никогда не вызовет его, вы не потеряете время зря. Если другой фрагмент кода вызывает его из нескольких мест, ваш синглтон может оптимизировать кеширование для обоих одновременно и избежать двойного поиска. Вы также можете легко избежать любого состояния взаимоблокировки на ресурсе, управляемом синглтоном.

Против одиночных игр:

  • В C++ нет удобного способа автоматической очистки после синглтонов. Есть обходные пути и несколько хитрые способы сделать это, но просто нет простого универсального способа убедиться, что деструктор вашего синглтона всегда вызывается. Это не так уж и ужасно с точки зрения памяти - просто подумайте об этом как о более глобальных переменных для этой цели. Но может быть плохо, если ваш синглтон выделяет другие ресурсы (например, блокирует некоторые файлы) и не освобождает их.

Мое собственное мнение:

Я использую синглтоны, но избегаю их, если есть разумная альтернатива. До сих пор это работало для меня хорошо, и я обнаружил, что они поддаются тестированию, хотя для тестирования немного больше работы.

В блоге Google Testing есть три довольно хороших поста о синглтонах Мишко Хевери .

  1. Синглтоны - патологические лжецы
  2. Куда делись все синглтоны?
  3. Основная причина синглтонов

Самая большая проблема с одноплодным что они делают модульное тестирование трудно , особенно , если вы хотите запустить тесты параллельно , но независимо.

Во-вторых, люди часто считают, что ленивая инициализация с блокировкой с двойной проверкой является хорошим способом их реализации.

Наконец, если ваши синглтоны не являются неизменяемыми, они могут легко стать проблемой производительности, если вы попытаетесь масштабировать приложение для работы в нескольких потоках на нескольких процессорах. Согласованная синхронизация стоит дорого в большинстве сред.

Одна пугающая вещь о синглтонах, например, в Java, заключается в том, что в некоторых случаях вы можете получить несколько экземпляров одного и того же синглтона. JVM однозначно идентифицирует на основе двух элементов: полного имени класса и загрузчика классов, ответственного за его загрузку.

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

Один из коллег, с которым я работал, был очень единомышленником. Всякий раз, когда появлялось что-то вроде менеджера или босса, он превращал это в синглтон, потому что считал, что босс должен быть только один. И каждый раз, когда к системе предъявлялись новые требования, выяснялось, что для разрешения нескольких экземпляров имелись вполне веские причины.

Я бы сказал, что синглтон следует использовать, если модель предметной области диктует (а не «предполагает»), что он существует. Все остальные случаи - просто единичные экземпляры класса.

Синглтон как деталь реализации - это нормально. Синглтон как интерфейс или как механизм доступа - это гигантский PITA.

Статический метод, не принимающий параметров, возвращающий экземпляр объекта, лишь немного отличается от простого использования глобальной переменной. Если вместо этого объект имеет ссылку на переданный одноэлементный объект либо через конструктор, либо через другой метод, то не имеет значения, как на самом деле создается одноэлементный объект, и оказывается, что весь шаблон не имеет значения.

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

Существует статический синглтон , в котором класс заставляет существовать только один экземпляр класса для каждого процесса (в Java фактически один для каждого ClassLoader). Другой вариант - создать только один экземпляр .

Статические синглтоны - это зло - это один из видов глобальных переменных. Они усложняют тестирование, потому что невозможно выполнить тесты в полной изоляции. Вам нужна сложная настройка и код разрыва для очистки системы между каждым тестом, и очень легко забыть правильно очистить какое-то глобальное состояние, что, в свою очередь, может привести к неопределенному поведению в тестах.

Создание только одного экземпляра - это хорошо . Вы просто создаете один экземпляр при запуске программы, а затем передаете указатель на этот экземпляр всем остальным объектам, которые в нем нуждаются. Фреймворки внедрения зависимостей упрощают это - вы просто настраиваете область действия объекта, а структура DI позаботится о создании экземпляра и передаче его всем, кто в нем нуждается. Например, в Guice вы должны аннотировать класс с помощью @Singleton, и инфраструктура DI создаст только один экземпляр класса (для каждого приложения - у вас может быть несколько приложений, работающих в одной JVM). Это упрощает тестирование, поскольку вы можете создать новый экземпляр класса для каждого теста и позволить сборщику мусора уничтожить экземпляр, когда он больше не используется. Никакое глобальное состояние не будет просачиваться из одного теста в другой.

Для получения дополнительной информации: The Clean Code Talks - «Глобальное состояние и синглтоны».

Напишите нормальные, тестируемые, инъекционные объекты и позвольте Guice / Spring / всем остальным обрабатывать их создание. Шутки в сторону.

Это применимо даже в случае кешей или любых других естественных вариантов использования синглтонов. Нет необходимости повторять ужас написания кода, чтобы попытаться применить один экземпляр. Позвольте вашей структуре внедрения зависимостей справиться с этим. (Я рекомендую Guice для легкого контейнера DI, если вы его еще не используете).

Синглтон - не ужасный паттерн, хотя его часто используют неправильно . Я думаю, что это неправильное использование связано с тем, что это один из самых простых шаблонов, и большинство новичков в синглтоне привлекаются к глобальному побочному эффекту.

Эрих Гамма сказал, что синглтон - это шаблон, который, как он хотел, не был включен в книгу GOF, и это плохой дизайн. Я склонен не соглашаться.

Если шаблон используется для создания единственного экземпляра объекта в любой момент времени, значит, шаблон используется правильно. Если синглтон используется для глобального эффекта, значит, он используется неправильно.

Недостатки:

  • Вы соединяетесь с одним классом по всему коду, который вызывает одноэлементный
    • Создает проблемы с модульным тестированием, потому что трудно заменить экземпляр имитирующим объектом.
    • Если код необходимо реорганизовать позже из-за необходимости в более чем одном экземпляре, это более болезненно, чем если бы одноэлементный класс был передан в объект (с использованием интерфейса), который его использует.

Преимущества:

  • В любой момент времени представлен один экземпляр класса.
    • По замыслу вы обеспечиваете это
  • Экземпляр создается при необходимости
  • Глобальный доступ - это побочный эффект

Цыпочки меня копают, потому что я редко использую синглтоны, а когда я использую, это обычно что-то необычное. Нет, серьезно, мне нравится шаблон singleton. Ты знаешь почему? Потому что:

  1. Я ленивый.
  2. Ничего не может пойти не так.

Конечно, «эксперты» будут болтать о «модульном тестировании» и «инъекции зависимостей», но это все по почкам динго. Вы говорите, что синглтон сложно тестировать? Без проблем! Просто объявите все публично и превратите свой класс в веселый дом мирового добра. Вы помните шоу «Горец» 1990-х годов? Синглтон похож на это, потому что: A. Он никогда не может умереть; и Б. Может быть только один. Так что перестаньте прислушиваться ко всем этим слабостям DI и без промедления реализуйте свой синглтон. Вот еще несколько веских причин ...

  • Все это делают.
  • Шаблон singleton делает вас непобедимым.
  • Синглтон рифмуется со словом «победа» (или «веселье» в зависимости от вашего акцента).

Я много читаю о "Синглтоне", его проблемах, о том, когда его использовать и т. Д., И вот мои выводы до сих пор:

  • Путаница между классической реализацией Singleton и реальным требованием: ИМЕТЬ ТОЛЬКО ОДИН ЭКЗЕМПЛЯР КЛАССА!

  • Это вообще плохо реализовано. Если вам нужен уникальный экземпляр, не используйте (анти) шаблон использования статического метода GetInstance (), возвращающего статический объект. Это заставляет класс отвечать за создание отдельного экземпляра самого себя, а также выполнять логику. Это нарушает принцип единой ответственности . Вместо этого это должно быть реализовано фабричным классом, который должен гарантировать, что существует только один экземпляр.

  • Он используется в конструкторах, потому что он прост в использовании и не должен передаваться в качестве параметра. Это должно быть решено с помощью внедрения зависимостей , что является отличным шаблоном для создания хорошей и тестируемой объектной модели.

  • Не TDD . Если вы выполняете TDD, зависимости извлекаются из реализации, потому что вы хотите, чтобы ваши тесты было легко писать. Это делает вашу объектную модель лучше. Если вы используете TDD, вы не будете писать статический GetInstance =). Кстати, если вы мыслите объектами с четкими обязанностями, а не классами, вы получите тот же эффект =).