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

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

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

Вот довольно хорошая статья по этому вопросу, хотя она против использования Singletons: http://scientificninja.com/advice/performant-singletons.

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

23 ответа

В защиту синглетонов:

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

Против синглетонов:

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

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

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

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

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

Более подробное объяснение см. В разделе " Почему синглтоны противоречивы" от Google.

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

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

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

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

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

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

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

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

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

Недостатки:

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

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

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

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

  1. Мне лень.
  2. Ничто не может пойти не так.

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

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

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

Синглтон должен использоваться только для одного экземпляра данного класса. Хранилище шаблонов имеет отличную информацию о Синглтоне.

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

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

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

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

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

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

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

Эта проблема привела ко многим переделкам для меня за один год.

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

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

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

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

Для получения дополнительной информации: Чистые переговоры по коду - "Глобальное государство и синглтоны"

Синглтон как деталь реализации в порядке. Синглтон в качестве интерфейса или в качестве механизма доступа представляет собой гигантскую PITA.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам