Переход от абстрактного интерфейса к конкретному классу
От: Michael Rusakov https://www.wincatalog.com
Дата: 26.06.07 03:00
Оценка:
Здравствуйте!

Имеется следующая программная структура. Ядро состоит из классов CItem (1), CSaveItemToDb (2) и CVisitor (3). Клиент может работать с классами (1) и (2) через интерфейсы IItem и ISaver соответсвенно. Класс CVisitor является полностью недоступным для пользователя и находится исключительно в ядре.

Для сохранения элемента CItem в БД, необходимо создать экземпляр класса CVisitor, проинициализировать его должным образом и передать классу CItem. Дабы не нагружать класс Item дополнительным "знанием" о БД, вся функциональность связанная с БД вынесена в класс CSaveItemToDb.

Итак, хотелось бы, чтобы клиент мог выполнять примерно такой код:
    IItem* pItem = //...
    ISaver* pSaver = //...
    
    pItem->setName(L"Вася");
    pSaver->saveItem(pItem);

При этом проблема заключается в том, что функции ISaver::saveItem передается указатель на интерфейс, а посетителю требуется указатель на класс (см. код ниже). То есть необходимо понижающее приведение типа.

Сразу скажу, что передать классу-посетителю (CVisitor) указатель на интерфейс нельзя, т.к. тогда надо выводить посетителя на уровень интерфейсов (чтобы добавить функцию accept в интерфейс IItem), а значит раскрывать кучу подробностей реализации элемента в частности и ядра в целом. Да и вообще весь шаблон "Посетитель" тогда теряет смысл.

Сдается мне, что я в чем-то не прав. Только пока не могу понять в чем именно. Буду благодарен за любые подсказки.

Примерный код интерфейсов и ядра:
//уровень интерфейсов
interface IItem
{
    virtual void setName(const wchar_t*) = 0;
    virtual const wchar_t* getName() const = 0;
}

interface ISaver
{
    virtual bool saveItem(IItem*) = 0;
}

//уровень ядра
class CItem : public IItem
{
public:
    CItem(){}
    virtual ~CItem(){} 
    
    virtual void setName(const wchar_t*) {/*...*/}
    virtual const wchar_t* getName() const {/*...*/}

    //Эта функция уровня ядра, 
    //она отсутствует в интерфейсе IItem
    void acceptVisitor(CVisitor *v){v->visit(this);}
};

class CVisitor
{
    //...
    void visit(CItem*);
    void setDatabase(CDb *pDb);
};

class CSaveItemToDb : public ISaver
{
public:
    //....
    virtual bool saveItem(IItem *pItem)
    {
        //Вот здесь нужно совершить переход 
        //от интерфейса к классу и выполнить

        CVisitor v;
        v->setDatabase(_pDb);
        CItem *p = ??? pItem;

        p->acceptVisitor(&v);

        return true;
    }
private:
    CDb *_pDb;
};
WinCatalog — Disk Catalog Software for Windows
Re: Переход от абстрактного интерфейса к конкретному классу
От: Константин Л. Франция  
Дата: 26.06.07 09:19
Оценка:
Здравствуйте, Michael Rusakov, Вы писали:

[]

Как я понял, visitor нужен для возможности сохранения разных наследников IItem? И различаются они свойствами? А може тогда сделать в IItem словарь свойств?

interface IItem
{
    virtual void setName(const wchar_t*) = 0;
    virtual const wchar_t* getName() const = 0;

    virtual const wchar_t* getProperty( const wchar_t* name ) const = 0;
    virtual void setProperty( const wchar_t* name, const wchar_t* value ) = 0;
}


тогда и необходимость в visitor'е отпадет.

имхо, по другому никак
Re: Переход от абстрактного интерфейса к конкретному классу
От: artelk  
Дата: 26.06.07 15:57
Оценка: 3 (1)
Здравствуйте, Michael Rusakov, Вы писали:

Может как-нибудь так...

//уровень интерфейсов
interface IItem
{
    virtual void setName(const wchar_t*) = 0;
    virtual const wchar_t* getName() const = 0;
};

class ItemFactory
{
public:
    static IItem* create(const wchar_t* ClassID);
};

interface ISaver
{
    virtual bool saveItem(IItem*) = 0;
};


//уровень ядра
interface IItemWithAccept: public IItem
{
    virtual void setName(const wchar_t*) = 0;
    virtual const wchar_t* getName() const = 0;
    virtual void acceptVisitor(CVisitor *v) = 0;
};

class CItem : public IItemWithAccept // и вообще все классы  сохраняемых объектов должны наследовать от IItemWithAccept
{
public:
    CItem(){}
    virtual ~CItem(){} 
    
    virtual void setName(const wchar_t*) {/*...*/}
    virtual const wchar_t* getName() const {/*...*/}

    virtual void acceptVisitor(CVisitor *v){v->visit(this);}
};

IItem* ItemFactory::create(const wchar_t* ClassID)
{
    //создаем объект класса-наследника IItemWithAccept и возвращаем его
}

class CVisitor
{
    //...
    void visit(CItem*);
    void setDatabase(CDb *pDb);
};

class CSaveItemToDb : public ISaver
{
public:
    //....
    virtual bool saveItem(IItem *pItem)
    {
        CVisitor v;
        v->setDatabase(_pDb);
        IItemWithAccept *p = dynamic_cast<IItemWithAccept*> (pItem);

        p->acceptVisitor(&v);

        return true;
    }
private:
    CDb *_pDb;
};
Re[2]: Переход от абстрактного интерфейса к конкретному клас
От: Michael Rusakov https://www.wincatalog.com
Дата: 27.06.07 01:17
Оценка:
Здравствуйте, artelk, Вы писали:

A>Здравствуйте, Michael Rusakov, Вы писали:


A>Может как-нибудь так...


Большое спасибо, отличная идея! Как же я не додумался создать "приватный" интерфейс и пронаследовать от него?

В общем, IItemWithAccept будет интерфейсом, от которого наследуется CItem, но этот интерфейс будет находиться только в ядре и поэтому в нем можно использовать внутренние классы.

Еще раз спасибо за идею, сейчас попробую реализовать.
WinCatalog — Disk Catalog Software for Windows
Re[2]: Переход от абстрактного интерфейса к конкретному клас
От: Michael Rusakov https://www.wincatalog.com
Дата: 27.06.07 01:21
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Как я понял, visitor нужен для возможности сохранения разных наследников IItem? И различаются они свойствами? А може тогда сделать в IItem словарь свойств?


Оно примерно так и реализовано, только IItem — это контейнер, содержащий классы свойств (немного модифицированный шаблон "Состояние"). Клиенты получают свойства как раз через словарь, а внутри они (свойства) сгруппированы в классах, грубо говоря, по классу на таблицу БД, из которой они читаются.
WinCatalog — Disk Catalog Software for Windows
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.