使用枚举和切换而不是访问者模式

时间:2016-08-03 09:46:43

标签: c++ enums visitor-pattern

我有一个表示数学表达式的树,我想为了计算表达式树的值,我会实现访问者模式,但是在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 ++中的访问者模式,所以它不需要所有这些重复吗?

(直接在stackoverflow中输入代码,对不起任何错误,但它基于实际代码)

编辑:对于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 个答案:

答案 0 :(得分:2)

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

// 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> >
{
  ...
};