Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: подклассы Singlton
Форум на CrossPlatform.RU > Разработка > С\С++
call_me_Frank
Привет!

не могу найти примера того, как нужно использовать подклассы Singlton'а Мейерса.
суть задачи проста: нужен абстрактный класс логгера, и несколько его реализаций - для файловой системы, для БД, для консоли и т.д. Конечно, сам Логгер на всю программу должен быть в ед. экземпляре.

Вот чего-то я запутался в этих трех соснах. :blink: Help me, please!
Iron Bug
как-то непонятно, чего ты хочешь. singleton всегда имеет один экземпляр. что значит "несколько реализаций"?
call_me_Frank
я планирую сделать так:

наследовать от QObject (юзаю Кьют) некий базовый подкласс (от которого наследовать остальные классы), в котором объявлен логгер, и потом где-то как-то выбирать конкретный логгер, используемый приложением:

// BASE.H

class BASE : public QObject

    LOGGER * logger;
    ...
};

// BASE.CPP

BASE::BASE(QObject) {

    switch(log_type){

        case 0: logger = FILE_LOGGER::get_instance();
        case 1: logger = DB_LOGGER::get_instance();
        case 2: logger = CONSOLE_LOGGER::get_instance();
    }

    ...

    logger->log("BINGO!");
};


FILE_LOGGER, DB_LOGGER & CONSOLE_LOGGER - соответственно, являются наследниками класса LOGGER с переопределенными методами log(const QString &msg);

появилось мнение, что с Мейерсом такой вариант не прокатит, и нужно использовать реализацию синглтона с указателем? или я вообще не в ту степь повернул?)
Iron Bug
ты пытаешься применить паттерн фабрики к синглтону. про паттерны можешь почитать здесь: http://www.wiki.crossplatform.ru/index.php/Design_Patterns
посмотри "Фабричный метод". там примерно то, что ты пытаешься изобразить, насколько я понимаю.
call_me_Frank
именно!

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

Собственно, я не могу понять: все классы (LOGGER & FILE_LOGGER, DB_LOGGER, ...) должны быть реализованы как Singleton, или не все? если FILE_LOGGER, наследует LOGGER и в классе BASE я использую интерфейс класса LOGGER, значит надо прописать в нем виртуальную ф-ию log(QString), для того, что бы переопределить её в FILE_LOGGER. Но если класс LOGGER является Singlton'ом, то значит ф-ия log() должна быть static...вот какие противоречия роятся у меня в голове :blink:
call_me_Frank
решил! 8)

// .H

#ifndef CLASS1_H
#define CLASS1_H

#include <iostream>

enum LOG_TYPE {LOG_C1 = 0, LOG_C2 = 1};

class class1;

class c1Dest
{
    class1 * _inst;
public:
    ~c1Dest();
    void init(class1 * p);
};

class class1
{
    static class1 * __inst;
    static c1Dest dest;

    static LOG_TYPE type;
protected:
    class1(){}
    class1(const class1&);
    class1& operator=(class1&);
    ~class1(){}

    friend class c1Dest;
public:
        static class1& instance();
    virtual void echo();
};

class class2 : public class1
{
public:
    void echo();
};

#endif // CLASS1_H


// .CPP

#include "class1.h"

LOG_TYPE class1::type = LOG_C1;

class1 * class1::__inst = 0;
c1Dest class1::dest;

c1Dest::~c1Dest(){
    delete _inst;
}

void c1Dest::init(class1 *p){
    _inst = p;
}

class1& class1::instance(){
    if (!__inst){
        switch (type){
            case LOG_C1: __inst = new class1; break;
            case LOG_C2: __inst = new class2; break;
        }
        dest.init(__inst);
    }
    return *__inst;
}

void class1::echo(){
    std::cout << "class1::echo() " << this << std::endl;
}

void class2::echo(){
    std::cout << "class2::echo() " << this << std::endl;
}


пригодна такая реализация? сделал, порадовался...теперь засомневался <_<

и появился вопрос: как развязать классы по отдельным h-файлам? получается зацикленное включение хедеров
call_me_Frank
Вот еще один вариант реализации с помощью шаблонов:

template<class T>
class SingleTonePattern {

public:
    static T& getInstance() {
        static T instance;
        return instance;
    }

    virtual void echo() = 0;
};

class CustomSingleTone : public SingleTonePattern<CustomSingleTone>
{
    friend class SingleTonePattern<CustomSingleTone>;
private:
    CustomSingleTone() {}

public:
    virtual void echo() {
       qDebug() << "ECHO!!";
    }
};


и вызов:

    CustomSingleTone &st = SingleTonePattern<CustomSingleTone>::getInstance();
    st.echo();


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

Цитата(call_me_Frank @ 25.2.2015, 19:26) *
и появился вопрос: как развязать классы по отдельным h-файлам? получается зацикленное включение хедеров

это стандартное решение в С/C++. каждый хэдер всегда заключён в блок препроцессорных определений:
#ifndef SOME_HEADER_NAME
#define SOME_HEADER_NAME
....
<код>
....
#endif

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

Цитата(call_me_Frank @ 25.2.2015, 17:26) *
и появился вопрос: как развязать классы по отдельным h-файлам? получается зацикленное включение хедеров


обычно так решают:
#ifndef SOMEHEADER_H
#define SOMEHEADER_H

#include <otherheader.h>

class SomeClass
{
};

#endif //SOMEHEADER_H
Iron Bug
Цитата(wiz29 @ 25.2.2015, 21:06) *
не совсем понятно, зачем этот огород?

это просто упражнение на технику программирования. весьма полезное, кстати.
wiz29
Цитата(call_me_Frank @ 25.2.2015, 16:17) *
именно!

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

Собственно, я не могу понять: все классы (LOGGER & FILE_LOGGER, DB_LOGGER, ...) должны быть реализованы как Singleton, или не все? если FILE_LOGGER, наследует LOGGER и в классе BASE я использую интерфейс класса LOGGER, значит надо прописать в нем виртуальную ф-ию log(QString), для того, что бы переопределить её в FILE_LOGGER. Но если класс LOGGER является Singlton'ом, то значит ф-ия log() должна быть static...вот какие противоречия роятся у меня в голове :blink:


В этом случае перечисленные (LOGGER & FILE_LOGGER, DB_LOGGER, ...) должны быть реализациями интерфейса логгирования. Пользователю вообще обычно нет дела, куда пишется, был бы интерфейс, который позволяет это делать.
Интерфейс не всегда означает присутствие виртуальных методов. В терминологии шаблонов - это просто наличие у класса определенных методов/полей.

Опять же, есть вариант при котором могут быть реализованы несколько объектов-одиночек. Каждый из которых будет выполнять свою задачу, либо вариант о котором я уже говорил: наличие некоторого "абстрактного" "устройства" вывода для отправки логов (на диск, в базу, в сеть и тп). Все зависит от целей.
call_me_Frank
Постараюсь ответить по-порядку )

Iron Bug, спасибо за пояснение насчет __var. В приложениях такие имена никогда не использую, это был тест на скорую руку, что бы проверить независимость переменных с одним и тем же именем в разных классах. почему-то меня взяли сомнения на этот счет :lol: это было самое простое и быстрое решение. но теперь есть причина тем более не делать этого.

да, согласен, решение с классами действительно получилось довольно странным. я его не буду использовать, но кое на чем хочу остановиться: как мне показалось, вы (и Iron Bug, и wiz29) не поняли моего вопроса насчет развязки хедеров. Дело в том, что нет возможности вынести class2 в отдельный файл - он зависит от class1, который в свою очередь использует class2 в своей функции instance(). Получаются перекрестные ссылки, и я не нашел решения, как их развязать. Что скажете, сможете предложить решение? или это я не понял ваших комментариев?

Цитата
не совсем понятно, зачем этот огород?

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

wiz29,
Цитата
Пользователю вообще обычно нет дела, куда пишется, был бы интерфейс, который позволяет это делать.
- именно в этом и состоит моя задача.

Цитата
Опять же, есть вариант при котором могут быть реализованы несколько объектов-одиночек.
- мне кажется, именно он и реализован во втором предложенном мной варианте с шаблонами, нет? Что скажете насчет такого решения?
lanz
Цитата
Получаются перекрестные ссылки, и я не нашел решения, как их развязать. Что скажете, сможете предложить решение? или это я не понял ваших комментариев?

Все нормально, нет перекрестных ссылок :lol:
Раскрывающийся текст
class1.h
class Class1 {
Class1 * instance();
...
}

class1.cpp
#include "class1.h"
#include "class2.h"
Class1 * Class1::instance() {
  ...
}

class2.h
#include "class1.h"
class Class2 : public Class1 { ...}

class2.cpp
#include "class2.h"
...
Iron Bug
в случае, если перекрёстные ссылки всё-таки есть (это должны быть именно ссылки, а не объекты класса), то стандартное решение состоит в использовании предварительной декларации класса:
class ClassB;  // предварительная декларация класса ClassB

class ClassA {
     ClassB *ptrB;
};

class ClassB {
    ClassA *ptrA;
    ClassA A;  // после полного определения класса мы можем использовать не только указатели, но и объекты класса
};
wiz29
Цитата(call_me_Frank @ 25.2.2015, 22:40) *
Что скажете насчет такого решения?


По хорошему, как было сказано вами же выше - наличие виртуального интерфейса не обязательно, особенно, если не планируется дать возможность пользователю менять поведение одиночки.
Реализацию же, всегда можно "скрыть" от непосредственных пользователей.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.