访问者模式的通用应用程序的接口类

时间:2014-07-03 13:16:31

标签: c++ templates design-patterns interface visitor

我遇到了一个可以通过访问者模式解决的问题,因为我喜欢可重复使用的代码,我认为让一些已经为我做了大部分魔术并且我可以在以后重复使用的类可能是一个好主意。所以我想要的是一个类访问者类和一个可访问类,我可以从中派生出来为使用访问者模式准备我的派生类。我想出了这段代码:

template <typename ret = void>
class Visitor
{
public:
    typedef ret ReturnType;

protected:
    Visitor() {}
    ~Visitor() {}
};

template <typename BaseType>
class Visitable
{
public:
    template <typename Visitor>
    typename Visitor::ReturnType applyVisitor(Visitor& visitor)
    {
        return visitor(static_cast<BaseType*>(this));
    }

    template <typename Visitor>
    typename Visitor::ReturnType applyVisitor(Visitor& visitor) const
    {
        return visitor(static_cast<BaseType*>(this));
    }

protected:
    Visitable() {}
    ~Visitable() {}
};

template <typename VisitorType, typename VisitableType>
inline typename VisitorType::ReturnType applyVisitor(VisitorType visitor, VisitableType visitable)
{
    return visitable->applyVisitor(visitor);
}

class Base : public Visitable <Base>
{
public:
    virtual void foo() const
    {
        std::cout << "BASE" << std::endl;
    };

    std::string foobar() const
    {
        return "BASE";
    };
};

class Derived : public Base, public Visitable<Derived>
{
public:
    using Visitable<Derived>::applyVisitor;

    void foo() const
    {
        std::cout << "DERIVED" << std::endl;
    }; 

    std::string bar() const
    {
        return "DERIVED";
    };
};

struct MyVisitor : public Visitor < >
{
    template <class T>
    void operator()(T const var) const
    {
        var->foo();
    }
};

struct MyOtherVisitor : public Visitor <std::string>
{
    std::string operator()(Base * const var) const
    {
        return var->foobar();
    }

    std::string operator()(Derived * const var) const
    {
        return var->bar();
    }
};

int main(int _Argc, char* _Argv)
{
    Base *pVirtualDerived = new Derived();
    Base *pBase = new Base();
    Derived *pDerived = new Derived();

    std::cout << "Member method:" << std::endl;

    applyVisitor(MyVisitor(), pVirtualDerived);
    applyVisitor(MyVisitor(), pBase);
    applyVisitor(MyVisitor(), pDerived);

    std::cout << std::endl << "External method:" << std::endl;

    std::cout << applyVisitor(MyOtherVisitor(), pVirtualDerived) << std::endl;
    std::cout << applyVisitor(MyOtherVisitor(), pBase) << std::endl;
    std::cout << applyVisitor(MyOtherVisitor(), pDerived) << std::endl;
}

正如人们可能已经从名字中猜到的那样,我受boost::static_visitorboost::variant的启发。但是,人们也可以注意到我的实现在两个方面存在缺陷:

  1. 仅仅从Visitable继承是不够的,我还需要在我的类中添加一个using声明来解决applyVisitor方法的歧义。
  2. 这不是真正的访客模式。使用实际指向applyVisitor对象的Base*来呼叫Derived不会调用Derived::foo,而是Base::foo。我无法在applyVisitor虚拟中声明Visitable<T>,因为它是模板化方法。但我需要模板,因为Visitor<T>是一个自己的模板类,我想保留访问者的通用返回类型。
  3. 长话短说,我可以以某种方式解决这两个问题并最终得到两个类,我只需要派生这些类来为访问者模式准备我的代码吗?

1 个答案:

答案 0 :(得分:0)

有很多关于访问者设计模式和C ++继承的讨论。在您描述的特定情况下,我认为解决方案是:

关于第一个问题,由于Base类已经从Visitable继承,因此您不需要在派生类中再次从它继承:

class Derived : public Base
{
public:
    void foo() const
    {
        std::cout << "DERIVED" << std::endl;
    };
};

关于第二个问题,我想你刚忘了Base类中的virtual关键字:

class Base : public Visitable<Base>
{
    public:
        virtual void foo() const
        {
            std::cout << "BASE" << std::endl;
        };
};