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

Ну, как вы знаете, шаблон проектирования Visitor имеет «проблему», аналогичную проблеме Abstract Factory: чем больше посещаемых классов я создал, тем более конкретные методы «посещения» должны быть созданы.

В случае абстрактной фабрики я создал решение, используя прототип продукта для «настройки» фабрики:

factory.h

class ExtensibleFactory
{
public:
~ExtensibleFactory();

void insertProductType(const string &nome, IProductPrototype *product);
void removeProductType(const string &nome);
IProductPrototype *createProduct(const string &nome);

private:
map<string, IProductPrototype *> m_productsHash;
};

factory.cpp

#include "extensiblefactory.h"
#include "iproductprototype.h"
ExtensibleFactory::~ExtensibleFactory()
{
for(map<string, IProductPrototype *>::iterator iter = this->m_productsHash.begin(); iter != this->m_productsHash.end(); ++iter)
{
delete iter->second;
}

this->m_productsHash.clear();
}

void ExtensibleFactory::insertProductType(const string &nome, IProductPrototype *product)
{
this->m_productsHash.insert(make_pair(nome, product));
}

void ExtensibleFactory::removeProductType(const string &nome)
{
delete this->m_productsHash[nome];
this->m_productsHash.erase(nome);
}

IProductPrototype *ExtensibleFactory::createProduct(const string &nome)
{
if ( this->m_productsHash.find(nome) == this->m_productsHash.end() )
{
return 0;
}

return this->m_productsHash[nome]->clone();
}

main.cpp

SanduichePrototype *sanduiche = new SanduichePrototype;
CarroPrototype *carro = new CarroPrototype;

ExtensibleFactory *fabrica = new ExtensibleFactory;

fabrica->insertProductType("sanduba", sanduiche);
fabrica->insertProductType("automovel", carro);

IProductPrototype *carro1 = fabrica->createProduct("automovel");
IProductPrototype *carro2 = fabrica->createProduct("automovel");

IProductPrototype *sanduiche1 = fabrica->createProduct("sanduba");
IProductPrototype *sanduiche2 = fabrica->createProduct("sanduba");

Теперь рассмотрим этого посетителя и его элементы:

ivisitor.h

class ElementA;
class ElementB;

class IVisitor
{
public:
virtual void visit(ElementA *elementA) = 0;
virtual void visit(ElementB *elementB) = 0;
};

ielement.h

class IVisitor;

class IElement
{
public:
virtual void accept(IVisitor *visitor) = 0;
};

elementa.h

class ElementA : public IElement
{
public:
virtual void accept(IVisitor *visitor);
};

elementb.h

class ElementB : public IElement
{
public:
virtual void accept(IVisitor *visitor);
};

Если я хочу добавить больше элементов, мне нужно будет добавить больше методов для интерфейса IVisitor.

Я хочу знать, возможно ли «настроить» посетителя во время выполнения, другими словами, я хочу знать, есть ли какое-либо решение для эмуляции добавления новых методов в интерфейс IVisitor путем его настройки, как я это сделал для Factory шаблон и, если так, какие будут возможные решения.

0

Решение

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

Сначала рассмотрим небольшой рефакторинг кода шаблона посетителя — за исключением общности и именования и доступа, он такой же, как и ваш код:

// Static visitor pattern.

template< class Visitable >
class Visitor_
{
template< class > friend class Visitable_impl_;
private:
virtual void visit( Visitable& ) {}
};

class A;
class B;

class I_visitor
: public Visitor_<A>
, public Visitor_<B>
{};

class I_visitable
{
public:
virtual void accept( I_visitor& ) = 0;
};

template< class Visitable >
class Visitable_impl_
: public I_visitable
{
public:
void accept( I_visitor& v )
override
{
static_cast<Visitor_<Visitable>&>( v )          // Cast for access.
.visit( static_cast<Visitable&>( *this ) ); // Cast for overload res.
}
};

class A: public Visitable_impl_<A> {};
class B: public Visitable_impl_<B> {};

#include <iostream>
using namespace std;

auto main()
-> int
{
class Action
: public I_visitor
{
private:
void visit( A& ) override { cout << "Visited an A." << endl; }
};

I_visitable&&       a   = A();
I_visitable&&       b   = B();
Action              x;

a.accept( x );  b.accept( x );
}

Теперь мы просто заменим первый static_cast с dynamic_castи вуаля:

// Dynamic visitor pattern.

template< class Visitable >
class Visitor_
{
template< class > friend class Visitable_impl_;
private:
virtual void visit( Visitable& ) {}
};

struct I_visitor { virtual ~I_visitor(){} };    // Note: no mention of A or B.

class I_visitable
{
public:
virtual void accept( I_visitor& ) = 0;
};

template< class Visitable >
class Visitable_impl_
: public I_visitable
{
public:
void accept( I_visitor& v )
override
{
if( auto p_visitor = dynamic_cast<Visitor_<Visitable>*>( &v ) )
{
p_visitor->visit( static_cast<Visitable&>( *this ) );   // Cast for overload res.
}
}
};

class A: public Visitable_impl_<A> {};
class B: public Visitable_impl_<B> {};

#include <iostream>
using namespace std;

auto main()
-> int
{
class Action
: public I_visitor
, public Visitor_<A>
{
private:
void visit( A& ) override { cout << "Visited an A." << endl; }
};

I_visitable&&       a   = A();
I_visitable&&       b   = B();
Action              x;

a.accept( x );  b.accept( x );
}
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]