如何在运行时配置访问者模式

How can I make a visitor pattern configurable in runtime?

本文关键字:访问者 模式 配置 运行时      更新时间:2023-10-16

正如您所知,设计模式Visitor有一个类似于抽象工厂问题的"问题":我创建的可访问类越多,必须创建的"访问"方法就越具体。

在一个抽象工厂的情况下,我使用产品原型来"配置"工厂:

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接口。

我想知道是否有可能在运行时"配置"访问者,换句话说,我想知道有没有任何解决方案可以像我对Factory模式所做的那样,通过配置来模拟向IVisitor接口添加更多方法的行为,如果有,那将是可能的解决方案。

您想要传递给对象的动作(访问者)对象必须至少具有一些可以使用其功能的公共基类的硬连接知识,然后在我看来,动态注册可访问类没有多大意义,因为您无论如何都需要动态调度,例如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 );
}