使用枚举和开关代替访问者模式 我有一棵树,代表一个数学表达式,我想为了计算表达式树的值,我将实现访问者模式,但是在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 */ } };
这也有一个问题,因为方法是虚拟的,所以不能有模板节点 有一个枚举似乎容易得多,每个节点有一个实例,您只需在一个方法而不是访问者模式中打开枚举使用枚举和开关代替访问者模式 我有一棵树,代表一个数学表达式,我想为了计算表达式树的值,我将实现访问者模式,但是在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 */ } };,c++,enums,visitor-pattern,C++,Enums,Visitor Pattern,这也有一个问题,因为方法是虚拟的,所以不能有模板节点 有一个枚举似乎容易得多,每个节点有一个实例,您只需在一个方法而不是访问者模式中打开枚举 class Node { enum NodeType { Constant, Variable } Node(NodeType type) : m_nodeType(type) {} NodeType m_nodeType; }; class ConstantNode { ConstantNode() : Node(C
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:
//...
}
}
我知道enum版本需要动态强制转换,但总的来说,似乎需要重复大量相同的代码,这意味着它将允许模板节点,例如(BinOpNode
)
可替代的是,在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));
模板
类二项式节点{
T m_op=T();
BinOpNode():节点(BinOp){}
};
//在计算中:
案例双节点:
BinOpNode*n=动态_转换(节点);
返回n->m_op(计算(n->m_左),计算(n->m_右));
您可以使用模板删除重复:
// 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 */ }
};
但这也被访问者模式完美地处理了;使用上面的模板代码:
class Visitor:
public visits<BinOpNode<int> >,
public visits<BinOpNode<double> >,
...
{
};
template<typename T> class BinOpNode:
public ConcreteNode<BinOpNode<T> >
{
...
};
类访问者:
公众访问,
公众访问,
...
{
};
模板类双节点:
公共节点
{
...
};
这似乎一点也不容易。重复的数量相同(大小写标签与虚拟函数)。您也没有解释您计划如何处理模板<代码>case BinOp:现在怎么办?重复次数少了,因为它不再需要所有的Accept函数。在这两种情况下都会有节点的构造函数。添加新访问者不需要重复。并避免忘记与switch/enum情况相反的类型。“不再需要所有接受函数”CRTP是您的朋友。还可以查看“非循环访问者”。不,这是一些我不熟悉的变体。它似乎构建在每个类的唯一标记上(这正是您的类型enum)。对于原始非循环访客搜索,请参阅Robert C Martin的一篇论文。
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> >
{
...
};