Войти
ПрограммированиеТерминыОбщее

Синглтон (Singleton)

Синглтон (Singleton, или шаблон проектирования Одиночка) — это шаблон проектирования, гарантирующий, что класс имеет только один экземпляр, и обеспечивающий глобальный доступ к этому экземпляру. Другими словами, синглтон — это усовершенствованный вариант глобальной переменной.

Преимущества синглтонов перед глобальными переменными:

1) Синглтон гарантирует, что будет создан только один экземпляр класса.

Например, классы Log, Renderer, FileManager (обычно любой менеджер в системе по сути синглтон) и т.п. могут существовать только в единственном экземпляре . Глобальная переменная не удовлетворяет этому требованию, так как раз мы можем создать один экземпляр класса, значит мы можем «наплодить» хоть сколько таких же экземпляров, что в большинстве случаев приведёт к краху программы. При использовании синглтона, мы просто делаем конструктор класса private и объявляем синглтон-контейнер другом этого класса.

2) При использовании синглтонов почти не нужно заботиться о порядке создания глобальных объектов.

Например, класс Renderer при конструировании использует класс Log, который в свою очередь при конструировании использует класс FileManager.

В 99% случаев если классы созданы не в том порядке (надо FileManager, Log и последним Renderer), программа упадёт ещё до входа в функцию main(). При использовании глобальных переменных, приходится вникать в код каждого из «глобальных» классов, чтобы понять как они взаимосвязаны и инициализировать их в правильном порядке. Причем, если глобальные переменные находятся в разных модулях программы, то порядок их создания не специфицирован! Эта проблема с лёгкостью решается при помощи синглтонов: объект в них конструируется только при первом обращении к нему. Проблемы могут возникнуть только если конструкторы нескольких классов «зациклены» друг на друга, но это всего лишь свидетельствует о «криворукости» программиста.

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

3) Не нужно заботиться об удалении глобальных объектов.

А что если при уничтожении, Renderer захочет сообщить что-либо архиважное классу Log, а того уже и нет в помине? Правильно, получится геморрой. Иногда надо, чтобы некоторые уничтоженные объекты снова «оживали» при надобности. С помощью обычных глобальных переменных эта проблема не решаема. У синглтонов и с этим всё в порядке: достаточно лишь «сказать» контейнеру, что если объект уже уничтожен, а к нему кто-то обращается, его нужно восстановить.

О синглтонах можно прочитать в книге Андрея Александреску «Современное проектирование на C++». В этой же книге приведён пример реализации класса SingletonHolder для библиотеки Loki (http://sourceforge.net/projects/loki-lib/). Эта реализация основана на стратегиях Strategy, всего их три:

1) Стратегия Creation — задаёт способ создания объекта при первом обращении к нему. Три предложенных варианта реализации:
CreateUsingNew (задан по умолчанию) — создаёт объект с помощью оператора new и конструктора по умолчанию.
CreateUsingMalloc — создаёт объект с помощью функции std::malloc и конструктора по умолчанию.
CreateStatic — создаёт объект в статической памяти.

2) Стратегия LifeTime — задаёт время жизни и уничтожения объекта. Четыре предложенных реализации:
DefaultLifeTime (задан по умолчанию) - объект удаляется в соответствии со стандартными правилами C++.
PhoenixSingleton — то же самое, что и DefaultLifeTime, но при попытке обращения к уничтоженному объекту, он создаётся снова.
SingletonWithLongevity — предполагает пользовательскую функцию, которая возвращает относительное время жизни объекта.
NoDestroy — объект не уничтожается средствами программы. Занимаемую им память «подчищает» операционная система после завершения работы программы.

3) Стратегия ThreadingModel — задаёт поведение внутреннего указателя на хранимый объект. Две реализации:
SingleThreaded (задан по умолчанию) — для использования в одном потоке.
ClassLevelLockable — для ипользования из нескольких потоков.
Кроме всех перечисленных реализаций, вы можете написать свою специфическую реализацию конкретной стратегии.

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

#include <loki/singleton.h>

class CLog;
typedef Loki::SingletonHolder<CLog, Loki::CreateStatic, Loki::PhoenixSingleton> Log;

class CLog
{
  friend struct Loki::CreateStatic<CLog>;
public:
  void OutText(const std::string &txt) {...};
private:
  //Защищаем наш класс
  CLog();
  CLog(const CLog &x);
  const CLog &operator=(const CLog &x);
  CLog *operator&() const;
  ~CLog();
};

//Использование
void MegaFunction()
{
  Log::Instance().OutText("Халлоу, чуваки!");
}

Ссылки:

Для расширенной информации о синглтонах читайте:
boost::singleton_ptr (код, из boost vault)
Singleton Pattern (англ)
Singletons Are Good (англ, коротко)
Singletons Are Evil (англ, длинно)
Singleton Considered Misleadingly Named (англ)
Почему синглетоны - отстой? (внимание: ненормативная лексика!)

Что такое Синглтон (Singleton)?

#паттерны, #шаблоны проектирования

17 декабря 2005 (Обновление: 23 апр. 2010)

Комментарии [51]