Оригинал: http://dblog.aldacron.net/2007/03/03/singletons-in-d/
Перевод: Крашенко Леонид, http://jetbird.wordpress.com.
Синглетоны в Ди.
Смысл паттерна «синглетон» описан в [1], а именно:
- гарантировать, что будет создан только 1 эксземпляр класса;
- предоставить глобальную точку доступа к данному экземпляру.
Синглетоны используются в ООП повсеместно. В данной заметке мы рассмотрим несколько путей реализации синглетона средствами языка Ди.
Первый способ описан в [1]:
class MySingleton
{
public:
static MySingleton instance()
{
if(_instance is null) _instance = new MySingleton;
return _instance;
}
private:
this() {}
static MySingleton _instance;
}
В этой реализации создается экземпляр MySingleton при первом вызове метода instance(); обратим внимание на несколько ключевых моментов.
Во-первых, здесь только 1 конструктор, и он private. Добавление новых конструкторов сделает такой класс бесполезным с точки зрения его изначального предназначения – пользователи смогут инстанциировать его самостоятельно. С другой стороны, если мы вообще уберем конструктор, это тоже не сработает, т.к. Ди автоматически создает конструктор по-умолчанию (и он public). Поэтому все синглетоны должны иметь private конструктор. В большинстве языков это гарантирует, что инстанциировать класс может только он сам. В Ди, как бы там ни было, private члены класса «видны» внутри модуля, в котором определены. Поэтому хорошей практикой является помещение синглетонов в отдельные модули. Если поместить их в окружающий код, надо следить за тем, чтобы случайно не инстанциировать синглетон больше, чем 1 раз. Хотя от этого можно защититься (см. далее).
Во-вторых, член _instance и метод instance() являются оба статическими. Вместе c private конструктором это позволяет классу осуществлять полный контроль над процессом инстанциирования.
В Java синглетоны иногда объявляются как public, а не как private, например:
class JavaSingleton {
public static final instance = new JavaSingleton();
private JavaSingleton() {}
}
Ключевое слово final гарантирует, что экземпляр будет инстанциирован только 1 раз. Попытки создать его повторно будут приводить к ошибкам во время компиляции. В Ди также есть ключевое слово final, но оно имеет несколько иной смысл. В Java final может применяться к объявлению класса, чтобы предотвратить дальнейшее наследование от него; или к объявлению члена класса с целью удостовериться, что она инстанциирована лишь 1 раз; или к методу, чтобы предотвратить его переопределение в классе-наследнике. В Ди (насколько я знаю) final применяется только для того, чтобы предотвратить переопределение метода в классе-наследнике (объявление класса как final всего лишь запрещает переопределение всех его методов в классах-наследниках). Члены класса в Ди, обявленные как final, могут быть переинициализированы. Так что final для этих задач использовать здесь нельзя. Надо использовать const.
Если вы объявляете статический объект как const, то вы должны использовать статический конструктор для его инициализации:
class MySingleton2
{
public:
static const MySingleton2 instance;
private:
this() {}
static this() { instance = new MySingleton2; }
}
Также имеется возможность инициализировать статические члены класса в месте объявления, но инициализирующее значение должно быть константным (например, static int x = 10; ). Инструкция new возвращает не константу, поэтому должно находиться в статическом конструкторе.
Статические конструкторы запускаются в Ди во время инициализации приложения (хотя код загрузки должен быть вызван вручную, если мы используем метод WinMain на платформе Windows). Поскольку instance инициализируется в статическом конструкторе, гарантируется, что это будет сделано до того, как будет вызван метод instance(). Более того, из-за того, что instance определен как static const, то он может быть инициализирован только в статическом конструкторе. Любая попытка инициализации извне приведет к ошибкам во время компиляции. Можно пометить член instance как private и дать ему метод-accessor («доставатель»), но в этом нет никакой необходимости, т.к. значение instance никогда не будет изменяться. Таким образом, если убрать private и const, то это может привести к ошибкам внутри модуля.
В Ди лучше использовать именно такой подход, если, конечно, не найдется причин использовать ленивое инстанциирование. В любом случае, обе реализации могут быть упакованы в шаблон.
Шаблонный вариант первой реализации:
class Singleton(T)
{
public:
static T instance()
{
if(_instance is null) _instance = new T;
return _instance;
}
private:
this() {}
static T _instance;
}
class TMySingleton : Singleton!(TMySingleton)
{
}
И второй:
class Singleton2(T)
{
public:
static const T instance;
private:
this() {}
static this() {instance = new T; }
}
class TMySingleton2 : Singleton!(TMySingleton2)
{
}
Эти 4 основные реализации Синглетона в Ди охватывают 99% всех возможного применения синглетонов. Какую версию использовать – решайте по ситуации. (…).
Исходный код некоторых примеров использования каждой из приведенных реализаций.