【发布时间】:2009-11-30 20:16:16
【问题描述】:
我有兴趣做以下类似的事情来遵守 Null 对象设计模式并避免大量的 NULL 测试:
class Node;
Node* NullNode;
class Node {
public:
Node(Node *l=NullNode, Node *r=NullNode) : left(l), right(r) {};
private:
Node *left, *right;
};
NullNode = new Node();
当然,正如所写,NullNode 在 Node 类声明之前和之后具有不同的内存位置。如果您不想使用默认参数(即删除 Node *r=NullNode),则可以在没有前向声明的情况下执行此操作。
另一种选择是使用一些继承:创建一个带有两个子类(NullNode 和 FullNode)的父类(Node)。那么上面的节点示例将是 FullNode 的代码,并且上面代码中的 NullNode 将是从 Node 继承的 NullNode 类型。我讨厌通过继承来解决简单的问题。
所以,问题是:如何在 C++ 中将 Null 对象模式应用于具有默认参数(即同一类的实例!)的递归数据结构(类)?
【问题讨论】:
-
您仍然需要将所有节点与
NullNode进行比较,以查看它们是否有效。这能为您节省什么? -
Donnie,一般的想法是,而不是:如果(NULL 测试)do nothing else do something。你会这样做: thisObject.doStuff() 如果 thisObject 是 NullObject,那么它(瞧!)什么也不做(也许它有许多空函数定义 {})。如果 thisObject 是一个“真正的节点”,那么它会做一些有用的事情(比如打印自己)。比较 NullNode::Print() {} 与 FullNode::Print{ cout
-
如果没有继承(并使一切都成为多态),难道每个Node方法都必须检查当前实例是否是NullNode,所以它们不会做任何事情吗?另一个问题:return 的方法呢?在这些情况下 NullNode 会返回什么?
-
查看 wikipedia 文章,在我看来,该模式的重点并不是消除任何 NULL 对象和/或异常。如果一个函数可以返回一个空字符串来表示“没有结果”,那么返回 NULL 可能没有多大意义。这并不意味着您不需要区分无结果和错误的情况。特别是,这种模式似乎不适合 ADT。考虑链表:所以它会以一个空对象终止?那么你会如何认清结局呢?对,你仍然需要测试指针。
-
关键是,任何用于表示“无结果”的“填充类型”不是我们正在使用的实际类型,都是“坏主意”。我把坏主意放在引号里,b/c 这就是索赔。这真的是一个权衡。如果您期望一个节点并且您得到 NULL、"" 或 0,您仍然需要检查它。如果你返回这个“东西”,那么你必须返回一个“无结果”和“有用的东西”的联合。使用继承和多态(顺便说一句,没有继承:您是否建议函数重载就足够了?),您删除了检查(if/then 或 try/except)。参见 Martin,敏捷软件开发,第 189 页。
标签: c++ recursion object null default