模板派生类的访问者模式

时间:2013-05-27 21:38:28

标签: c++ templates derived visitor-pattern

相关问题:link

在上述问题的一个答案中,我建议使用访问者模式来解决我的类继承结构的一些问题。但是,我不确定是否可以在我的上下文中使用它,因为我的派生类可以是非类型模板。

为了展示问题,我使用了来自此来源的修改后的代码:http://sourcemaking.com/design_patterns/visitor/cpp/2。 下面的示例无法编译,因为无法定义虚拟模板方法。但是,我相信,代码演示了我想要实现的目标。有没有替代方案来解决这个问题?

// 1. Add an accept(Visitor) method to the "element" hierarchy
class Element
{
  public:
    virtual void accept(class Visitor &v) = 0;
};

template <unsigned int N>
class This: public Element
{
  public:
     /*virtual*/void accept(Visitor &v);
    string thiss()
    {
        return "This";
    }
};

class That: public Element
{
  public:
     /*virtual*/void accept(Visitor &v);
    string that()
    {
        return "That";
    }
};

// 2. Create a "visitor" base class w/ a visit() method for every "element" type
class Visitor
{
  public:
    template<unsigned int N>
    virtual void visit(This<N> *e) = 0;
    virtual void visit(That *e) = 0;

};

 template<unsigned int N>
 /*virtual*/void This<N>::accept(Visitor &v)
{
  v.visit(this);
}

 /*virtual*/void That::accept(Visitor &v)
{
  v.visit(this);
}

// 3. Create a "visitor" derived class for each "operation" to do on "elements"
class UpVisitor: public Visitor
{
     /*virtual*/void visit(This *e)
    {
        cout << "do Up on " + e->thiss() << '\n';
    }
     /*virtual*/void visit(That *e)
    {
        cout << "do Up on " + e->that() << '\n';
    }

};

class DownVisitor: public Visitor
{
     /*virtual*/void visit(This *e)
    {
        cout << "do Down on " + e->thiss() << '\n';
    }
     /*virtual*/void visit(That *e)
    {
        cout << "do Down on " + e->that() << '\n';
    }

}; 

    int main() 
    {

        Element *list[] = 
        {
            new This<3>(), new That()
        };
        UpVisitor up; // 4. Client creates
         DownVisitor down; //    "visitor" objects
         for (int i = 0; i < 2; i++) list[i]->accept(up);
         for (int i = 0; i < 2; i++) list[i]->accept(down);
    }

1 个答案:

答案 0 :(得分:2)

问题是你的Visitor类与派生自Element的类紧密结合。随着您扩展设计,这将比现有的更多。您可以通过提供定义可访问对象的所有要求的“目标”类来减少/消除正确的耦合。由于派生类的名称是一个公共属性,因此您也可以将存储和访问权限放入目标类中。

// 1. Define out visitor and destination interfaces
struct Destination
{
    Destination(const std::string& name) : name_(name) {}
    virtual std::string ident() const { return name_; }

    const std::string name_;
};

struct Visitor
{
    virtual void visit(Destination *e) = 0;
};

这样可以将访问者的要求与Element类分开,这似乎是您的意图。然后,您的ThisThat类继承自Destination并提供必要的实施。

// 2. Define our element and it's derived classes
class Element
{
public:
    virtual void accept(class Visitor &v) = 0;
};

template <unsigned int N>
class This: public Element, public Destination
{
public:
    This() : Destination("This") {}
    virtual void accept(Visitor &v)
    {
        v.visit(this);
    }
};

class That: public Element, public Destination
{
public:
    That() : Destination("That") {}
    virtual void accept(Visitor &v)
    {
        v.visit(this);
    }
};

现在,您的上下游客被简化为以下内容

// 3. Create a "visitor" derived class for each "operation" to do on "elements"
class UpVisitor: public Visitor
{
    void visit(Destination *e) {
        cout << "do Up on " + e->ident() << '\n';
    }
};

class DownVisitor: public Visitor
{
    void visit(Destination *e) {
        cout << "do Down on " + e->ident() << '\n';
    }
}; 

虽然我没有在上面的解决方案中更改它,但我建议更改visit以获取引用而不是指针。由于C ++没有空引用的概念,这表明Destination必需的,因为指针可以被视为可选