C ++: замена для синглтона - PullRequest
1 голос
/ 14 июля 2011

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

Class Foo
{
    A* a;
    B* b;
    C* c;
};

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

Вместо этого я могу сделать Foo синглтоном (конечно, только если может быть только 1 экземпляр), а затем просто вызвать Foo :: getInstance () -> ... из A, B и C. И у меня нет пропускать любые предметы. Я нахожу это очень удобным.

Теперь проблема в том, что у меня может быть несколько случаев Foo. Очевидно, я не могу использовать шаблон синглтона. Но я не хочу передавать переменные и хранить их в классах-членах. Слишком много кода! :)

Как пример, Я ненавижу это:

A::A(Foo* foo) : m_foo(foo)
{

}

и это:

void A::someFunc(Foo* foo, int someParam)
{

}

Но я люблю это:

A::A()
{

}

void A::someFunc(int someParam)
{
    Foo* foo = Foo::getInstance();
}

Есть ли другой способ сделать это? Что-то, что напоминает синглтон?

Ответы [ 4 ]

7 голосов
/ 14 июля 2011

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

1 голос
/ 14 июля 2011

Сделай свой синглтон class a template.

template<unsigned int NUMBER>
class Foo
{
  A* a;
  B* b;
  C* c;
};

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

Foo<1>;
Foo<2>;
Foo<3>;
0 голосов
/ 14 июля 2011

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

Похоже, вам нужен глобальный, так что используйте глобальный.

A* g_a;
B* g_b;
C* g_c;

Инициализируйте все глобальные переменные в main, прежде чем что-либо еще должно получить к ним доступ.

Или умнее.

template< typename T >
T& instance( void );

template< typename T >
void set_instance( T& t );

void needs_an_A( void )
{
   instance<A>().a_stuff();
}

Или лучше всего связать все это с RAII:

void needs_a_b( void )
{
   B& b = instance<B>();
   b.stuff();
   b.more_stuff();
}

int main()
{
   Initializer<A> init_a;
   Initializer<B> init_b;  // B needs an A during construction
   Initializer<C> init_c( "C constructor param" );

   needs_a_b();
}
0 голосов
/ 14 июля 2011

Я сделал это в нескольких проектах.Вот некоторый псевдокод, чтобы дать вам представление о том, как я это сделал:

static map<string, Foo*> instances;

static Foo* Foo::getInstance(string name)
{
    Foo* inst = NULL;

    lock(instances);
    if(instances.count(name) > 0)
    {
        inst = instances[name];
    }
    else
    {
        inst = new Foo();
        instances[name] = inst;
    }

    unlock(instances);
    return inst;
}

Затем вы просто вызываете Foo :: getInstance ("instance1") или Foo :: getInstance ("instance2") и т. Д.Все, что вам нужно сделать, это запомнить строку, довольно мило (я думаю).Я не знаю, есть ли официальное название для этого шаблона проектирования, если есть кто-то, пожалуйста, скажите мне, чтобы я не казался настолько невежественным в будущем при описании этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...