【发布时间】:2016-08-03 09:46:42
【问题描述】:
我有一个表示数学表达式的树,我想为了计算表达式树的值,我会实现访问者模式,但是在 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));
【问题讨论】:
-
这似乎并不容易。重复的数量是相同的(案例标签与虚函数)。你也没有解释你打算如何处理模板。
case BinOp:现在呢? -
重复次数更少,因为它消除了对所有 Accept 函数的需要。在任何一种情况下,节点都会有构造函数。
-
添加新访客不需要重复。并避免忘记与 switch/enum 大小写相反的类型。
-
"不再需要所有 Accept 函数" CRTP 是您的朋友。还要查找“非循环访问者”。
-
不,这是我不熟悉的一些变体。它似乎建立在每个类的唯一标签上(这正是您的类型枚举)。对于原始的非循环访问者,请搜索 Robert C Martin 的论文。
标签: c++ enums visitor-pattern