Здравствуйте!
Имеется следующая программная структура. Ядро состоит из классов 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;
};
Здравствуйте, 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'е отпадет.
имхо, по другому никак
Здравствуйте, 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;
};
Здравствуйте, artelk, Вы писали:
A>Здравствуйте, Michael Rusakov, Вы писали:
A>Может как-нибудь так...
Большое спасибо, отличная идея! Как же я не додумался создать "приватный" интерфейс и пронаследовать от него?
В общем,
IItemWithAccept будет интерфейсом, от которого наследуется CItem, но этот интерфейс будет находиться только в ядре и поэтому в нем можно использовать внутренние классы.
Еще раз спасибо за идею, сейчас попробую реализовать.