2013-11-24 2 views
1

У меня есть одноэлементный класс для ведения журнала в моем проекте Qt. В каждом классе, кроме одноэлементного, есть указательный пункт для объекта singleton и сигнал, подключенный к слоту записи в одноэлементном объекте. Какой бы класс не захотел написать информацию о журнале, просто испустите этот сигнал. Сигналы помещаются в очередь, поэтому они потокобезопасны.Singleton класс по всему проекту подход

Пожалуйста, критикуйте этот подход с точки зрения ООП, спасибо.

=============================================================================================================================================== =========================================== Редактировать 1 : Спасибо вам всем, что вам нравится, прислушиваясь к противоположным мнениям, это всегда большое обучение.

Позвольте мне подробнее рассказать о моем подходе и о том, что я сделал в своем коде: Точно так же, как указатель MikeMB, у singleton-класса есть статическая функция, такая как get_instance(), которая возвращает ссылку на этот синглтон. Я сохранил его в локальном указателе в конструкторе каждого класса, поэтому он будет уничтожен после возвращения конструктора. Это удобно для проверки того, получил ли я нулевой указатель и делает код более читаемым. Мне не нравится то, как это:

if(mySingletonClass::gerInstance() == NULL) { ... } 
connect(gerInstance(), SIGNAL(write(QString)), this, SLOT(write(QString))); 

, потому что это дороже, чем это:

QPointer<mySingletonClass> singletonInstance = mySingletonClass::getInstance(); 
if(singletonInstance.isNull) { ... } 
connect(singletonInstance, SIGNAL(write(QString)), this, SLOT(write(QString))); 

Вызов функции в два раза дороже, чем создание локальной переменной с точки ASM зрения из-за толчок, выбор адреса и возврата адреса.

Вот мой синглтон класс:

class CSuperLog : public QObject 
{ 
    Q_OBJECT 

public: 
     // This static function creates its instance on the first call 
     // and returns it's own instance just created 
     // It only returns its own instance on the later calls 
     static QPointer<CSuperLog> getInstance(void); // 
     ~CSuperLog(); 

public slots: 
    void writingLog(QString aline); 

private: 
    static bool ready; 
    static bool instanceFlag; 
    static bool initSuccess; 
    static QPointer<CSuperLog> ptrInstance; 

    QTextStream * stream; 
    QFile * oFile; 
    QString logFile; 

    explicit CSuperLog(QObject *parent = 0); 
}; 

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

MikeMB:

Your approach is making a middle man sitting in between, it makes the path of the logging info much longer because the signals in Qt are always queued except you make direct connection. The reason why I can't make direct connection here is it make the class non-thread-safe since I use threads in each other classes. Yes, someone will say you can use Mutex, but mutex also creates a queue when more than one thread competing on the same resource. Why don't you use the existing mechanism in Qt instead of making your own? 

Спасибо все ваши посты!

=============================================================================================================================================== ===========

Edit 2:

Марселю Blanck:

  1. Мне нравится ваш подход, а потому, что вы считали конкурс ресурсов.
  2. Практически в каждом классе мне нужны сигналы и слоты, поэтому мне нужен QObject, и именно поэтому я выбираю Qt.
  3. Должен быть только один экземпляр для одного статического объекта, если я не ошибаюсь.
  4. Использование семафоров такое же, как использование сигналов/слотов в Qt, оба генерируют очередь сообщений.
  5. Всегда есть плюсы и минусы относительно шаблона проектирования программного обеспечения и производительности приложения. Добавление большего количества слоев между ними делает ваш код более гибким, но значительно снижает производительность на тех устройствах с более низким уровнем настройки, что делает ваше приложение зависящим от одного из самых мощных аппаратных средств, и именно поэтому большинство современных ОС написаны в чистых C и ASM. Как сбалансировать их - действительно большая проблема.

Не могли бы вы объяснить немного больше о вашем статическом заводе-изготовителе Logger? Благодарю.

+0

Для чего нужен указатель на синглтон? – MikeMB

+0

Чтобы подключить сигнал в другом объекте к слоту записи в этом одноэлементном объекте. – QtFan

+1

[Почему одиночные игры плохие] (http://stackoverflow.com/a/138012/2642204) – BartoszKP

ответ

1

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

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

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

+0

Спасибо за ваш ответ! Я думаю, что после моего проекта был создан объект singleton в main(), и я вставлял его в другие классы внутри своих конструкторов. – QtFan

+0

@QtFan, хорошо, тогда это действительно не должно быть одноэлементным? Если вы просто пытаетесь контролировать, что создается только один экземпляр, лучше с Factory. – Brady

1

Одноэлемент обычно имеет статическую функцию, например get_instance(), которая возвращает ссылку на этот синглтон, поэтому вам не нужно хранить указатель на singleton в каждом объекте.

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

Btw: Есть уже довольно продвинутые библиотеки протоколирования, поэтому вы должны выяснить, можете ли вы использовать один из них или, по крайней мере, как они используются и адаптировать эту концепцию к вашим потребностям.

==========================

EDIT 1 (ответ на EDIT 1 из QtFan):

К сожалению , по-видимому, я пропустил понимание, потому что я думал, что указатель будет членом класса, а не только локальной переменной в конструкторе, которая, конечно, прекрасна.

Позвольте мне пояснить, что я имел в виду, делая связь на более высоком уровне: Это была направлена ​​исключительно на то, где вы делаете связь - то есть, где вы положили линии

connect(gerInstance(), SIGNAL(write(QString)), this, SLOT(write(QString))); 

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

void main() { 
    create Thread1 
    create Thread2 
    create Thread3 

    create Logger 

    connect Thread1 signal to Logger slot 
    connect Thread2 signal to Logger slot 
    connect Thread3 signal to Logger slot 

    run Thread1 
    run Thread2 
    run Thread3 
} 

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

Конечно, это возможно только в том случае, если вы не создаете свои объекты/потоки динамически во время выполнения программы. Он также не работает, если вы хотите регистрироваться во время создания ваших объектов.

+0

Спасибо, Майк! Благодарим за ваше предложение. На самом деле у меня есть несколько потоков, реализованных так, как вы предложили, и это именно то, что мне нравится. Это делает код более понятным. Но, к сожалению, в большинстве моих классов QObject, включая классы потоков, мне приходится записывать что-то вроде запроса ресурсов, создания сокетов и других критических вещей в своих конструкторах. Эти вещи нужно регистрировать каждый раз, когда они терпят неудачу. Еще раз спасибо за ваш вклад :-) – QtFan

2

Мне не нравятся одиночные игры, потому что всегда нечисто использовать их. Я даже прочитал описания должностных обязанностей, которые говорят «Знание шаблонов проектирования, зная, что Синглтон не тот, который нужно использовать».Синглтон приводит к зависти ад, и если вы когда-либо захотите перейти на совершенно другой подход к регистрации (mabe для тестирования или производства), не уничтожая старый, вам нужно многое изменить.

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

  • Каждый объект входа от потребности быть QObject
  • Если охота врезается ваши последние журналы не будут печататься потому что логгер не успел сделать это, прежде чем программа потерпела крах.

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

Также убедитесь, что ваш подход печатает напрямую. Даже printf записывает в буфер перед печатью, и вам нужно каждый раз промывать его, или вы никогда не сможете найти аварии при плохих обстоятельствах, если будете охотиться за ними.

Только мои 2 цента.

+0

См. Раздел «Редактировать 2» выше, спасибо. – QtFan

Смежные вопросы