如何使访问者模式在运行时可配置?
How can I make a visitor pattern configurable in runtime?
嗯,如你所知,设计模式 Visitor 有一个 "problem" 类似于抽象工厂问题:我创建的可访问性越高 类,必须创建的方法越具体 "visit" .
在抽象工厂的情况下,我使用产品原型为 "configure" 工厂做了一个解决方案:
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 接口方法。
我想知道是否可以在运行时 "configure" 访问者,换句话说,我想知道是否有任何解决方案可以通过配置来模拟向 IVisitor 接口添加更多方法的行为就像我对工厂模式所做的一样,如果是这样,这将是可能的解决方案。
你想传递给对象的动作(访问者)对象必须至少有一些共同基础的知识class它可以使用的功能,然后在我看来没有太多指向动态 registering 可访问 classes,因为无论如何你都需要动态调度,例如dynamic_cast
,因此无需在公共访问者界面中列出所有支持的 classes。
首先考虑对您的访问者模式代码进行轻微重构——除了通用性和命名和访问外,它与您的代码相同:
// 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 );
}
嗯,如你所知,设计模式 Visitor 有一个 "problem" 类似于抽象工厂问题:我创建的可访问性越高 类,必须创建的方法越具体 "visit" .
在抽象工厂的情况下,我使用产品原型为 "configure" 工厂做了一个解决方案:
factory.hclass 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.hclass 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 接口方法。
我想知道是否可以在运行时 "configure" 访问者,换句话说,我想知道是否有任何解决方案可以通过配置来模拟向 IVisitor 接口添加更多方法的行为就像我对工厂模式所做的一样,如果是这样,这将是可能的解决方案。
你想传递给对象的动作(访问者)对象必须至少有一些共同基础的知识class它可以使用的功能,然后在我看来没有太多指向动态 registering 可访问 classes,因为无论如何你都需要动态调度,例如dynamic_cast
,因此无需在公共访问者界面中列出所有支持的 classes。
首先考虑对您的访问者模式代码进行轻微重构——除了通用性和命名和访问外,它与您的代码相同:
// 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 );
}