C ++ Singletons: насколько хорошо это решение?Преимущества / недостатки, альтернативы - PullRequest
0 голосов
/ 14 декабря 2018

Я работаю над проектом C ++, имеющим несколько классов, которые должны быть синглетонами, с зависимостями между ними (порядок инициализации имеет значение).

Я придумала это решение:

  1. Все классы, которые я хочу быть синглетонами, имеют защищенные конструкторы , например:
class MySingleton1
{
protected:
    MySingleton1();
}
Иметь исходный файл singleton_factory.cpp , содержащий экземпляр класса Singletons , который является производным от всех классов, которые я хочу быть синглетонами , например:
#include "MySingleton1.hpp"
#include "MySingleton2.hpp"

class Singletons : public MySingleton1, public MySingleton2 {}
static Singletons s_singletons;
Все еще в singleton_factory.cpp , для каждого типа синглтона , также реализуйте специализацию функции getSingleton :
template<>
MySingleton1& getSingleton()
{
    return s_singletons;
}

template<>
MySingleton2& getSingleton()
{
    return s_singletons;
}
Специализации getSingleton будут "спрятаны" под общим шаблонным вариантом в singleton_factory.hpp :
template <class TSingleton>
TSingleton& getSingleton();

Преимущества:

  • Слабая связь :

    • Синглтон-классам не нужно быть «осведомленными»из класса Singletons , единственное, что нужно скрывать их конструктор под защищенным классификатором (и это даже не обязательно, только хорошая практика).Единственный код, действительно осведомленный о классе Singletons, - это singleton_factory.cpp
    • Тощие зависимости для конкретного экземпляра : код, который хочет использовать синглтон типа T нужно только включить заголовок типа T и тощий singleton_factory.hpp
  • Порядок инициализации может бытьуправляется изменением порядка наследования класса Singletons

  • Нет отложенной инициализации => поточно-ориентированный ?
  • getSingleton () быстро, нет dynamic_cast, нет reinterpret_cast

Недостатки:

  • Каждый раз, когда новыйПоявляется тип синглтона, специализация getSingleton , делающая то же самое - т. е. « return s_singletons; » необходимо добавить в singleton_factory.cpp

Итак, насколько я вижу, это на самом деле довольно хорошо так что я думаю оставить этоке, но я спрашиваю ваши отзывы (что может быть лучше, чем сообщество программистов?).

Какие дополнительные преимущества / недостатки вы делаетевидите с помощью этого решения?

Какие альтернативы вы предлагаете?

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

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

0 голосов
/ 14 декабря 2018

Это вызывает централизацию Singletons, которая может испортить зависимости в более сложных проектах.Библиотека с singleton.cpp должна зависеть от всего, что необходимо для каждого синглтона .В то же время любой, кто использует синглтон, должен зависеть от библиотеки singleton.cpp.

По сути, ваш код может работать только в монолитном немодульном проекте.Масштабировать его до нескольких динамических библиотек практически невозможно.

Ваш порядок инициализации должен поддерживаться вручную.

Точка построения статической глобальной переменной не чередуется со всем до первого выражения в main.


Приличное решение, которое я использовал, заключается в создании динамической библиотеки, которая хранит одноэлементную память.

Чтобы быть одноэлементным, вы наследуете от помощника CRTP, который обеспечивает::Instance() встроенный метод.Люди, которые хотят использовать синглтон, ::Instance().

::Instance() создают статический токен времени жизни локальной переменной.Затем он пытается получить хранилище для синглтона из первичной DLL;если объект уже создан, он просто преобразует хранилище в тип объекта и увеличивает его счетчик ссылок.

Если нет, он создает новое хранилище и создает в нем объект.

Atуничтожение токена времени жизни статической локальной переменной уменьшает количество ссылок.Если этот счетчик ссылок достигает 0, он уничтожает его локально в текущей динамической библиотеке.

Время жизни синглтона теперь является объединением времени жизни созданных ::Instance() переменных.Разрушение происходит в коде без стирания типа, поэтому нам не нужно беспокоиться о DLL с выгружаемым кодом.Хранение является центральным.Библиотека DLL, в которой хранится хранилище, должна быть на более низком уровне, чем каждый пользователь системы Singleton, но, в свою очередь, не имеет никаких зависимостей, так что это не проблема.

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

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