2016-08-03 72 views
2

我有一棵树表示一个数学表达式,我想为了计算表达式树的值,我会实现访问者模式,但是在C++中这涉及到很多重复自己,因为接受访问者的方法必须在每个子类上,因为即使方法是相同的,类型也不是。使用枚举和开关代替访问者模式

class Node { 
    virtual void Acccept(Visitor *visitor) = 0; 
}; 
class ConstantNode : Node { 
    virtual void Accept(Visitor *visitor) { 
    visitor->visit(this); 
    } 
}; 

class VariableNode : Node { 
    virtual void Accept(Visitor *visitor) { 
    visitor->visit(this); 
    } 
}; 

class Visitor { 
    virtual void visit(ConstantNode *node) = 0; 
    virtual void visit(VariableNode *node) = 0; 
}; 
class CalculateVisitor : Visitor { 
    virtual void visit(ConstantNode *node) { /* code */ } 
    virtual void visit(VariableNode *node) { /* code */ } 
}; 

这也有问题,因为方法是虚拟的,你不能有模板节点。

这似乎有一个枚举,其中有一个每个节点的情况下,你只需打开一个方法,而不是访客模式的枚举。

class Node { 
    enum NodeType { 
    Constant, 
    Variable 
    } 
    Node(NodeType type) : m_nodeType(type) {} 
    NodeType m_nodeType; 
}; 

class ConstantNode { 
    ConstantNode() : Node(Constant) {} 
}; 
class VariableNode { 
    VariableNode() : Node(Variable) {} 
}; 

int calculate(Node* node) { 
    switch (node->m_nodeType) { 
    case Constant: 
     //... 
    case Variable: 
     //... 
    } 
} 

我知道枚举版本将需要动态铸造,但总体而言,似乎最好要重复很多相同的代码,它意味着将允许模板节点如(BinOpNode<std::plus>)。

或者,有一些方法可以改进C++中的访问者模式,以便它不需要所有这些重复?

(键入的代码直接到计算器,对不起任何错误,但它是基于真正的代码)

编辑:为BinOpNode:

template<class T> 
class BinOpNode { 
    T m_op = T(); 
    BinOpNode() : Node(BinOp) {} 
}; 

//in calculate: 
case BinOpNode: 
    BinOpNode* n = dynamic_cast<BinOpNode*>(node); 
    return n->m_op(calculate(n->m_left), calculate(n->m_right)); 
+1

它似乎并不容易。重复的数量是相同的(案例标签vs虚拟功能)。你也没有解释你计划如何处理模板。 '情况BinOp:'现在怎么样? –

+0

重复性较低,因为它不需要所有的Accept功能。在任何情况下都会有节点的构造函数。 –

+0

添加新访客不需要重复。并避免忘记与switch/enum case相反的类型。 “ – Jarod42

回答

1

您可以通过使用模板删除重复:

// Classes implementing the mechanism 

class Node { 
    virtual void Accept(Visitor *visitor) = 0; 
}; 

template<typename NodeType> class ConcreteNode 
{ 
    virtual void Accept(Visitor* visitor) 
    { 
    visits<NodeType>* v = visitor; 
    v->visit((NodeType*)this); 
    } 
}; 

template<typename NodeType> class visits 
{ 
    virtual void visit(NodeType* node) = 0; 
}; 

// The actual visitors/visited classes 

class Visitor: 
    visits<ConstantNode>, 
    visits<VariableNode> 
{ 
}; 

class ConstantNode : ConcreteNode<ConstantNote> {}; 
class VariableNode : ConcreteNode<VariableNode> {}; 

class CalculateVisitor : Visitor { 
    virtual void visit(ConstantNode *node) { /* code */ } 
    virtual void visit(VariableNode *node) { /* code */ } 
}; 

请注意,您的模板节点代码将而不是工作,因为下面的行不能工作:

BinOpNode* n = dynamic_cast<BinOpNode*>(node); 

BinOpNode您定义为模板,并且不能有指向模板的指针,只能指向类的指针。您可能,例如,有一个指针从BinOpNode产生特定类,像

BinOpNode<int>* n = dynamic_cast<BinOpNode<int>*>(node); 

但这是完全由访问者模式处理为好;与上面的模板代码:

class Visitor: 
    public visits<BinOpNode<int> >, 
    public visits<BinOpNode<double> >, 
    ... 
{ 
}; 

template<typename T> class BinOpNode: 
    public ConcreteNode<BinOpNode<T> > 
{ 
    ... 
};