2010-08-26 6 views
4

У меня есть требование иметь только один экземпляр класса в любой данный момент времени. Синглтон - очевидный кандидат. Но у меня есть некоторые другие условия, которые не характерны для Синглтона.C++ Singleton design question

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

Так моя реализация имеет следующие статические функции -

// To be called exactly once, everytime I enter the state 
void MySingleton::CreateInstance(size_t count); 

// To be called any no. of times during the state 
MySingleton * MySingleton::GetInstance(); 

// To be called exactly once, when leaving the state. 
void MySingleton::DestroyInstance();   

Сейчас эта реализация является одним из основного объезда от традиционной реализации одноплодной.

Есть ли проблемы с такой реализацией?

Есть ли лучшие альтернативы?

ответ

12

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

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

На мой взгляд, это было бы более чистым, чем предпочтительнее использовать шаблон с одиночным дизайном (анти).

+0

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

0

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

void MySingleton::reset(size_t count); 
5

Есть ли какие-либо проблемы с такой реализацией?

Да. Проблема в том, что это не синглтон, потому что это не то, что у модели «Singleton». Это простая переменная, локальная для этого состояния, в которую вы входите и уходите.

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

+0

Glorified Global, я согласен ... Но у меня есть абсолютно 0 проблем, использующих их вместо globals для инкапсуляции логики, которую несколько модулей должны использовать на одних и тех же данных. Я согласен с тем, что шаблон здесь не подходит. –

+0

+1 для уменьшения проблемы до ее сути: переменная является локальной для состояния, которая может быть даже определенной областью. –

+2

@San Jacinto: Я с удовольствием соглашусь с тем, что Singleton часто лучше, чем явные глобальные переменные. Обычно вы не должны использовать Singleton или глобальные переменные. –

2

Концепция вы описываете может быть обеспечена классом, как это:

template<class Guarded> 
class single_instance : private boost::noncopyable { 
    static bool alive; 
public: 
    single_instance() { 
     if (alive) 
     throw std::runtime_error("instance already exists"); 
     alive = true; 
    } 
    ~single_instance() { 
     alive = false; 
    } 
}; 

template<class Guarded> 
bool single_instance<Guarded>::alive = false; 

Если вы черпаете свой класс из этого, будет обеспечено, что вы всегда можете создать только один экземпляр в то время:

class myclass : single_instance<myclass> { 
public: 
    myclass(size_t count); 
    ... 
}; 

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

+0

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

+3

@san: noncopyable переводит непосредственно в закрытый, неопределенный экземпляр ctor & operator =. Тем не менее, это намного сложнее. Не стесняйтесь воображать 'private: T (const T & other);/* не определено */T & operator = (const T & other);/* не определено */'всякий раз, когда вы видите' boost :: noncopyable'. – Bill

+0

@San: Как сказал Билл, «boost :: noncopyable» - это только удобство. Вы также можете просто вручную убедиться, что класс не может быть скопирован, сделав свой конструктор копирования закрытым (и, для полноты, возможно, также оператором присваивания). Основной темой ответа является остальная часть этого шаблона, а не наследование от 'noncopyable'. – sth

1

Если это одно для состояния, то сделайте это изнутри объекта состояния и сделайте конструктор закрытым, состояние друга.

1

Я полностью согласен с тем, что «Singleton - это еще одна форма глобального», и этого следует избегать.

Если ваш босс настаивает, вы можете попробовать что-то вроде этого:

class MyUtil 
{ 
    protected: 
    static MyUtil* m_Singleton = NULL; 
    public: 
    /// Create the singleton if does not exist. 
    /// \return the singleton for this class. 
    static GetSingleton() 
    { 
     if(!m_Singleton ) 
     m_Singleton = new MyUtil(); 
     return m_Singleton ; 
    } 

    /// release singleton instance 
    /// Warning: Multi-threading not supported. 
    void StateRelease() 
    { 
     m_Singleton = NULL; 
     delete this; 
    } 
};