我遇到了一个代码重复的问题,关于树的访问者模式。现在的情况是这样的:我有一棵树,由两个不同的节点类组成,即叶节点和非叶节点。另外,我有两个访问者基类,除了一个访问const树和另一个访问非const树之外,它们看起来非常相似。具体访问者必须执行的实际操作与节点的具体类型无关。我举一个简短的例子:
class Visitor;
class ConstVisitor;
class Node {
public:
virtual void accept(Visitor&) = 0;
virtual void accept(ConstVisitor&) const = 0;
};
class Leaf : public Node {
virtual void accept(Visitor& v) {v.visitLeaf(*this);}
virtual void accept(ConstVisitor& cv) {cv.visitLeaf(*this);}
};
class CompoundNode : public Node {
public:
vector<Node*> getChildren() const;
virtual void accept(Visitor& v) {v.visitCompoundNode(*this);}
virtual void accept(ConstVisitor& cv) {cv.visitCompoundNode(*this);}
};
class Visitor {
protected:
virtual void processNode(Node& node) = 0;
public:
void visitLeaf(Leaf& leaf) {
processNode(leaf);
}
void visitCompoundNode(CompoundNode& cNode) {
processNode(cNode);
auto children = cNode.getChildren();
for (auto child : children)
child->accept(this);
}
};
class ConstVisitor {
protected:
virtual void processNode(Node const& node) = 0;
public:
void visitLeaf(Leaf const& leaf) {
processNode(leaf);
}
void visitCompoundNode(CompoundNode const& cNode) {
processNode(cNode);
auto children = cNode.getChildren();
for (auto child : children)
child->accept(this);
}
};
具体的访问者类要么继承Visitor
,要么继承ConstVisitor
,这取决于它们的processNode
方法是否需要改变所访问的节点。
你看,两个访问器之间有很多重复的代码,因为我将不得不实现另一种遍历策略,同样针对const和非const节点,我想避免重复。是否有可能提取重复的代码,最好不使用const_cast
到处?
你可以定义一个TVisitor
类模板,如下所示:
#include <type_traits>
class Node;
class CompoundNode;
class Leaf;
template<bool isNonConstVisitor>
class TVisitor
{
typedef typename std::conditional<isNonConstVisitor,
Node, Node const>::type node_type;
typedef typename std::conditional<isNonConstVisitor,
CompoundNode, CompoundNode const>::type compound_node_type;
typedef typename std::conditional<isNonConstVisitor,
Leaf, Leaf const>::type leaf_node_type;
protected:
virtual void processNode(node_type& node) = 0;
public:
void visitLeaf(leaf_node_type& leaf) { processNode(leaf); }
void visitCompoundNode(compound_node_type& cNode) {
processNode(cNode);
auto children = cNode.getChildren();
for (auto child : children) { child->accept(*this); }
}
};
然后使用Visitor
和ConstVisitor
作为该类模板的相应实例化的类型别名:
typedef TVisitor<true> Visitor;
typedef TVisitor<false> ConstVisitor;
你可以使用模板:
template<typename NodeType,
typename CompoundNodeType,
typename LeafType>
class BaseVisitor {
protected:
virtual void processNode(NodeType& node) = 0;
public:
void visitLeaf(LeafType& leaf) {
processNode(leaf);
}
void visitCompoundNode(CompoundNodeType& cNode) {
processNode(cNode);
auto children = cNode.getChildren();
for (auto child : children)
child->accept(this);
}
};
class Visitor: public BaseVisitor<Node, CompoundNode, Leaf> {
};
class ConstVisitor: public BaseVisitor<const Node, const CompoundNode, const Leaf> {
};