(注意:我正在寻找有关正确搜索词的任何建议,以阅读此类问题。 “对象关系映射”对我来说是一个我可以找到一些好的现有技术的地方......但是我还没有看到任何适合这种情况的东西。)
我有一个非常通用class Node
的 ,目前您可以认为它有点像 DOM 树中的一个元素。这并不是正在发生的事情——它们是内存映射文件中的图形数据库对象。但是对于所有实际目的,这个类比都相当接近,所以为了简单起见,我将坚持使用 DOM 术语。
嵌入在节点中的“标签”意味着您应该(理想情况下)能够使用它进行的一组特定操作。现在我正在使用派生类来做到这一点。例如,如果您尝试表示 HTML 列表之类的内容:
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
底层树将是七个节点:
+--UL // Node #1
+--LI // Node #2
+--String(Coffee) // Node #3 (literal text)
+--LI // Node #4
+--String(Tea) // Node #5 (literal text)
+--LI // Node #6
+--String(Milk) // Node #7 (literal text)
由于getString()
已经是节点本身的原始方法,我可能只会制作class UnorderedListNode : public Node
, class ListItemNode : public Node
.
继续这个假设,让我们想象一下,当程序员对他们手中的节点“类型”/标签有更多了解时,我想帮助他们使用不太通用的函数。也许我想帮助他们处理树上的结构习语,比如将字符串项添加到无序列表中,或者将内容提取为字符串。 (这只是一个类比,所以不要太认真地对待例程。)
class UnorderedListNode : public Node {
private:
// Any data members someone put here would be a mistake!
public:
static boost::optional<UnorderedListNode&> maybeCastFromNode(Node& node) {
if (node.tagString() == "ul") {
return reinterpret_cast<UnorderedListNode&>(node);
}
return boost::none;
}
// a const helper method
vector<string> getListAsStrings() const {
vector<string> result;
for (Node const* childNode : children()) {
result.push_back(childNode->children()[0]->getText());
}
return result;
}
// helper method requiring mutable object
void addStringToList(std::string listItemString) {
unique_ptr<Node> liNode (new Node (Tag ("LI"));
unique_ptr<Node> textNode (new Node (listItemString));
liNode->addChild(std::move(textNode));
addChild(std::move(liNode));
}
};
向这些新的派生类添加数据成员是个坏主意。真正持久化任何信息的唯一方法是使用 Node 的基本例程(例如,addChild
上面的调用或getText
)与树进行交互。因此,真正的继承模型——就存在的程度而言——是在 C++ 类型系统之外的。是什么让一个<UL>
节点“maybeCast”成为一个UnorderedListNode
与 vtables/etc 无关。
C++ 继承有时看起来不错,但通常感觉不对。我觉得我应该拥有独立于 Node 的类而不是继承,并且只是以某种方式与它协作作为“访问器助手”......但我不太了解那会是什么样子。