(Note:我正在寻找有关正确搜索词的任何建议来阅读此类问题。“对象关系映射” http://en.wikipedia.org/wiki/Object-relational_mapping我想到了一个可以找到一些好的现有技术的地方......但我还没有看到任何非常适合这个场景的东西。)
我有一个非常通用的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)
Since 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));
}
};
将数据成员添加到这些新的派生类中是一个坏主意。唯一的方法是really持久化任何信息的方法是使用 Node 的基本例程(例如,addChild
拨打上面的电话,或者getText
)与树交互。因此,真正的继承模型(就存在的程度而言)位于 C++ 类型系统之外。是什么让一个<UL>
节点“maybeCast”到UnorderedListNode
与 vtables/etc 无关。
C++ 继承有时看起来是正确的,但通常感觉是错误的。我觉得我应该拥有独立于 Node 存在的类,而不是继承,并且只是以某种方式作为“访问器助手”与它协作......但我不太清楚那会是什么样子。