Изменить: Из другого вопроса я предоставил ответ, который содержит ссылки на множество вопросов / ответов о синглетонах: Подробнее о синглетонах здесь:
Итак, я прочитал ветку Singletons: хороший дизайн или костыль?
И аргумент все еще неистовствует.
Я вижу Singletons как шаблон дизайна (хороший и плохой).
Проблема синглтона не в шаблоне, а в пользователях (извините всех). Все и их отец думают, что могут правильно их реализовать (а из многих интервью, которые я провел, большинство людей не могут). Кроме того, потому что все думают, что они могут реализовать правильный Singleton, они используют шаблон и используют его в ситуациях, которые не подходят (замена глобальных переменных на Singletons!).
Итак, основные вопросы, на которые необходимо ответить:
- Когда следует использовать синглтон
- Как правильно реализовать Singleton?
Я надеюсь, что в этой статье мы сможем собрать вместе в одном месте (вместо того, чтобы гуглить и искать на нескольких сайтах) авторитетный источник того, когда (а затем и как) правильно использовать Singleton. Также уместным будет список Анти-Использований и распространенных плохих реализаций, объясняющих, почему они не работают, и для хороших реализаций их слабые стороны.
Так что
держите мяч в руках : я подниму руку и скажу, что это то, что я использую, но, вероятно, есть проблемы.
Мне нравится "Скотт Майерс", рассматривающий эту тему в своих книгах "Эффективный C ++"
Хорошие ситуации, чтобы использовать Singletons (не много):
- Каркасы логирования
- Бассейны для утилизации ниток
/*
* C++ Singleton
* Limitation: Single Threaded Design
* See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
* For problems associated with locking in multi threaded applications
*
* Limitation:
* If you use this Singleton (A) within a destructor of another Singleton (B)
* This Singleton (A) must be fully constructed before the constructor of (B)
* is called.
*/
class MySingleton
{
private:
// Private Constructor
MySingleton();
// Stop the compiler generating methods of copy the object
MySingleton(MySingleton const& copy); // Not Implemented
MySingleton& operator=(MySingleton const& copy); // Not Implemented
public:
static MySingleton& getInstance()
{
// The only instance
// Guaranteed to be lazy initialized
// Guaranteed that it will be destroyed correctly
static MySingleton instance;
return instance;
}
};
ХОРОШО. Давайте получим некоторую критику и другие реализации вместе.
:-)
источник
Ответы:
Вы все не правы. Прочитайте вопрос. Ответ:
Используйте Singleton, если:
Не используйте Singleton, если:
Как создать лучший синглтон:
источник
Синглтоны дают вам возможность комбинировать две плохие черты в одном классе. Это неправильно почти во всех отношениях.
Синглтон дает вам:
Номер один прост. Глобалы вообще плохие. Мы никогда не должны делать объекты глобально доступными, если нам это действительно не нужно.
Номер два может звучать так, как будто это имеет смысл, но давайте подумаем об этом. Когда в последний раз вы ** случайно * создавали новый объект вместо ссылки на существующий? Поскольку это тег C ++, давайте использовать пример из этого языка. Вы часто случайно пишете
Когда вы намеревались написать
Конечно нет. Нам не нужна защита от этой ошибки, потому что такого рода ошибки просто не происходят. Если это так, то правильным ответом будет пойти домой и поспать 12-20 часов и надеяться, что вы почувствуете себя лучше.
Если нужен только один объект, просто создайте один экземпляр. Если один объект должен быть глобально доступен, сделайте его глобальным. Но это не значит, что нельзя создавать другие экземпляры этого.
Ограничение «возможен только один экземпляр» на самом деле не защищает нас от возможных ошибок. Но это делает наш код очень сложным для рефакторинга и поддержки. Потому что довольно часто мы узнаем позже, что нам нужно более одного экземпляра. У нас есть более одной базы данных, у нас есть более одного объекта конфигурации, нам нужно несколько регистраторов. Наши модульные тесты могут захотеть создавать и воссоздавать эти объекты каждый тест, чтобы взять общий пример.
Таким образом, синглтон должен использоваться тогда и только тогда, когда нам нужны обе черты, которые он предлагает: если нам нужен глобальный доступ (что редко, потому что глобальные списки обычно не поощряются), и мы должны помешать кому- либо создавать более одного экземпляра класс (который звучит для меня как вопрос дизайна). Единственная причина, по которой я это вижу, заключается в том, что создание двух экземпляров повредит нашему состоянию приложения - возможно, потому, что класс содержит несколько статических членов или аналогичную глупость. В этом случае очевидный ответ - исправить этот класс. Это не должно зависеть от того, чтобы быть единственным экземпляром.
Если вам нужен глобальный доступ к объекту, сделайте его глобальным, например
std::cout
. Но не ограничивайте количество экземпляров, которые могут быть созданы.Если вам абсолютно необходимо ограничить количество экземпляров класса до одного, и нет никакого способа, которым создание второго экземпляра можно было бы безопасно обработать, то примените это. Но не делайте это глобально доступным также.
Если вам нужны обе эти черты, то 1) сделайте их синглтоном и 2) дайте мне знать, для чего вам это нужно, потому что мне трудно представить такой случай.
источник
Проблема с синглетонами не в их реализации. Дело в том, что они объединяют два разных понятия, ни одно из которых явно не желательно.
1) Singletons предоставляют механизм глобального доступа к объекту. Хотя они могут быть несколько более поточно-ориентированными или более надежными в языках без четко определенного порядка инициализации, это использование по-прежнему является моральным эквивалентом глобальной переменной. Это глобальная переменная, одетая в какой-то неуклюжий синтаксис (скажем, foo :: get_instance () вместо g_foo), но она служит той же цели (один объект, доступный во всей программе) и имеет те же недостатки.
2) Синглтоны предотвращают множественные экземпляры класса. Это редкость, IME, что такого рода функции должны быть включены в класс. Обычно это намного более контекстная вещь; многие вещи, которые рассматриваются как «один-единственный-один», на самом деле просто-таки-случаются один-единственный. IMO более подходящее решение - просто создать только один экземпляр - пока вы не поймете, что вам нужно более одного экземпляра.
источник
Одна вещь с шаблонами: не обобщайте . У них есть все случаи, когда они полезны, и когда они терпят неудачу.
Синглтон может быть неприятным, когда вам нужно проверить код. Как правило, вы застряли с одним экземпляром класса и можете выбирать между открытием двери в конструкторе или каким-либо методом сброса состояния и так далее.
Другая проблема заключается в том, что Синглтон на самом деле является не более чем скрытой глобальной переменной . Когда у вас слишком много общего общего состояния в вашей программе, все возвращается назад, мы все это знаем.
Это может усложнить отслеживание зависимостей . Когда все зависит от вашего синглтона, его сложнее изменить, разделить на два и т. Д. Обычно вы застряли с этим. Это также препятствует гибкости. Изучите некоторые рамки внедрения зависимостей, чтобы попытаться облегчить эту проблему.
источник
Синглтоны в основном позволяют вам иметь сложное глобальное состояние в языках, которое иначе затрудняет или делает невозможным использование сложных глобальных переменных.
В частности, Java использует синглтоны в качестве замены глобальных переменных, поскольку все должно содержаться в классе. Наиболее близкими к глобальным переменным являются публичные статические переменные, которые можно использовать так, как если бы они были глобальными с
import static
В C ++ есть глобальные переменные, но порядок вызова конструкторов глобальных переменных класса не определен. Таким образом, синглтон позволяет вам отложить создание глобальной переменной до тех пор, пока эта переменная не понадобится в первый раз.
Такие языки, как Python и Ruby, очень мало используют синглтоны, потому что вы можете использовать глобальные переменные внутри модуля.
Так когда же хорошо / плохо использовать синглтон? В значительной степени именно тогда, когда было бы хорошо / плохо использовать глобальную переменную.
источник
Современный C ++ Design от Alexandrescu имеет поточно-ориентированный наследуемый универсальный синглтон.
Для моей 2р-стоимости, я думаю, важно определить время жизни ваших синглетонов (когда это абсолютно необходимо для их использования). Обычно я не позволяю статической
get()
функции создавать что-либо и оставляю настройку и удаление некоторому выделенному разделу основного приложения. Это помогает выделить зависимости между синглетами, но, как подчеркивалось выше, лучше всего избегать их, если это возможно.источник
Есть одна проблема, о которой я никогда не упоминал, с которой я столкнулся на предыдущей работе. У нас были синглтоны C ++, которые были разделены между DLL, и обычная механика обеспечения того, чтобы единственный экземпляр класса просто не работал. Проблема в том, что каждая DLL получает свой собственный набор статических переменных вместе с EXE. Если ваша функция get_instance встроенная или является частью статической библиотеки, каждая DLL будет иметь свою собственную копию «singleton».
Решение состоит в том, чтобы убедиться, что одноэлементный код определен только в одной DLL или EXE, или создать одноэлементный менеджер с этими свойствами для разделения экземпляров.
источник
Первый пример не является потокобезопасным - если два потока вызывают getInstance одновременно, то static будет PITA. Некоторая форма мьютекса поможет.
источник
Как уже отмечали другие, основными недостатками синглетонов являются невозможность их расширения и потеря способности создавать экземпляры более чем одного экземпляра, например, для целей тестирования.
Некоторые полезные аспекты одиночных игр:
Тем не менее, вам не нужно использовать синглтон, чтобы получить эти преимущества. Вы можете написать нормальный объект, который выполняет эту работу, и затем дать людям доступ к нему через фабрику (отдельный объект). Фабрика может беспокоиться только о том, чтобы создать его экземпляр, использовать его и т. Д., Если это необходимо. Кроме того, если вы программируете интерфейс, а не конкретный класс, фабрика может использовать стратегии, то есть вы можете включать и выключать различные реализации интерфейса.
Наконец, фабрика поддается технологиям внедрения зависимостей, таким как Spring и т. Д.
источник
Singletons удобны, когда у вас много кода, запускаемого при инициализации и объекте. Например, когда вы используете iBatis при настройке объекта персистентности, он должен прочитать все конфиги, проанализировать карты, убедиться, что все правильно, и т. Д., Прежде чем перейти к вашему коду.
Если бы вы делали это каждый раз, производительность сильно снизилась бы. Используя его в одиночку, вы принимаете этот удар один раз, а затем все последующие вызовы не должны это делать.
источник
Настоящим недостатком синглетонов является то, что они ломают наследство. Вы не можете получить новый класс для расширенной функциональности, если у вас нет доступа к коду, на который ссылается Singleton. Таким образом, помимо факта, что Singleton сделает ваш код тесно связанным (это можно исправить с помощью паттерна стратегии ... он же Dependency Injection), он также не позволит вам закрывать части кода от пересмотра (разделяемые библиотеки).
Так что даже примеры регистраторов или пулов потоков недопустимы и должны быть заменены стратегиями.
источник
Большинство людей используют синглтоны, когда пытаются заставить себя чувствовать себя лучше при использовании глобальной переменной. Существуют законные применения, но в большинстве случаев, когда люди используют их, тот факт, что может быть только один экземпляр, является тривиальным фактом по сравнению с тем, что он доступен во всем мире.
источник
Поскольку синглтон позволяет создавать только один экземпляр, он эффективно контролирует репликацию экземпляров. например, вам не понадобится несколько экземпляров поиска - например, карта поиска Морзе, поэтому можно обернуть ее в одноэлементный класс. И только то, что у вас есть один экземпляр класса, не означает, что вы также ограничены количеством ссылок на этот экземпляр. Вы можете ставить в очередь вызовы (чтобы избежать проблем с потоками) к экземпляру и вносить необходимые изменения. Да, общая форма синглтона является общедоступной, вы можете изменить дизайн, чтобы создать синглтон с более ограниченным доступом. Я не уставал раньше, но уверен, что это возможно. И для всех тех, кто прокомментировал, что шаблон синглтона является абсолютно злым, вы должны знать это:
источник
Но когда мне нужно что-то вроде синглтона, я часто заканчиваю тем, что использую счетчик Шварца для его создания.
источник
Ниже приведен лучший подход для реализации одноэлементного шаблона, безопасного для потоков, с освобождением памяти в самом деструкторе. Но я думаю, что деструктор должен быть необязательным, потому что экземпляр singleton будет автоматически уничтожен после завершения программы:
Относительно ситуаций, когда нам нужно использовать одноэлементные классы, можно: если мы хотим поддерживать состояние экземпляра на протяжении всего выполнения программы, если мы вовлечены в запись в журнал выполнения приложения, в котором требуется только один экземпляр файла. быть использованы .... и так далее. Было бы заметно, если кто-нибудь может предложить оптимизацию в моем коде выше.
источник
Я использую Singletons в качестве собеседования.
Когда я прошу разработчика назвать некоторые шаблоны проектирования, если они могут назвать только Singleton, они не нанимаются.
источник
Anti-Usage:
Одна из основных проблем, связанных с чрезмерным использованием синглтонов, заключается в том, что шаблон предотвращает простое расширение и замену альтернативных реализаций. Имя класса жестко закодировано везде, где используется синглтон.
источник
Я думаю, что это самая надежная версия для C #:
Вот оптимизированная версия .NET :
Вы можете найти этот шаблон на dotfactory.com .
источник
Паттерн Мейерса синглтона работает достаточно хорошо в большинстве случаев, и в некоторых случаях он не обязательно стоит того, чтобы искать что-то лучше. Пока конструктор никогда не сгенерирует и не будет никаких зависимостей между синглетонами.
Singleton - это реализация глобально доступного объекта (GAO отныне), хотя не все GAO являются синглетонами.
Сами регистраторы не должны быть одиночными, но в идеале средства для регистрации должны быть глобально доступны, чтобы отделить, где генерируется сообщение журнала, откуда и как оно регистрируется.
Ленивая загрузка / отложенная оценка - это другое понятие, и синглтон, как правило, также реализует это. У него много собственных проблем, в частности, безопасности потоков и проблем, если он терпит неудачу с исключениями, так что то, что казалось хорошей идеей в то время, оказывается не таким уж великим. (Немного похоже на реализацию COW в строках).
Имея это в виду, GOA можно инициализировать так:
Это не нужно делать так грубо, и явно в загруженной библиотеке, содержащей объекты, вы, вероятно, захотите, чтобы какой-то другой механизм управлял их временем жизни. (Поместите их в объект, который вы получите при загрузке библиотеки).
Что касается того, когда я использую синглтоны? Я использовал их для двух вещей - одноэлементная таблица, которая указывает, какие библиотеки были загружены с помощью dlopen; - обработчик сообщений, на который регистраторы могут подписаться, и на который можно отправлять сообщения. Требуется специально для обработчиков сигналов.
источник
Я до сих пор не понимаю, почему синглтон должен быть глобальным.
Я собирался создать синглтон, где я спрятал базу данных внутри класса как частную постоянную статическую переменную и создал функции класса, которые используют базу данных, даже не раскрывая базу данных пользователю.
Я не понимаю, почему эта функциональность была бы плохой.
источник
Я считаю их полезными, когда у меня есть класс, который содержит много памяти. Например, в недавней игре, над которой я работал, у меня есть класс карты влияния, который содержит коллекцию очень больших массивов непрерывной памяти. Я хочу, чтобы все выделялось при запуске, все освобождалось при выключении, и я определенно хочу только одну его копию. Я также должен получить доступ к нему из многих мест. Я считаю, что шаблон синглтона очень полезен в этом случае.
Я уверен, что есть и другие решения, но я считаю это очень полезным и простым в реализации.
источник
Если вы тот, кто создал синглтон и использует его, не делайте его синглтоном (это не имеет смысла, поскольку вы можете контролировать особенность объекта, не делая его синглтоном), но это имеет смысл, когда вы разработчик библиотека, и вы хотите предоставить только один объект для ваших пользователей (в этом случае вы - тот, кто создал синглтон, но вы не пользователь).
Синглтоны - это объекты, поэтому используйте их как объекты, многие люди обращаются к синглетонам напрямую, вызывая метод, который его возвращает, но это вредно, потому что вы заставляете свой код знать, что объект одиночный, я предпочитаю использовать синглтоны в качестве объектов, я передаю их через конструктор, и я использую их как обычные объекты, поэтому ваш код не знает, являются ли эти объекты одиночными или нет, и это делает зависимости более ясными, и это немного помогает для рефакторинга ...
источник
В настольных приложениях (я знаю, только мы, динозавры, пишем их больше!) Они необходимы для получения относительно неизменных глобальных настроек приложения - языка пользователя, пути к файлам справки, пользовательских настроек и т. Д., Которые в противном случае должны были бы распространяться на каждый класс и каждый диалог. ,
Изменить - конечно, они должны быть только для чтения!
источник
Еще одна реализация
источник
Instance()
следует возвращать указатель, а не ссылку. Внутри вашего.cpp
файла инициализации экземпляра пустое значение:Singleton* Singleton::instance_ = nullptr;
. ИInstance()
должны быть реализованы в виде:if (instance_ == nullptr) instance_ = new Singleton(); return instance_;
.