目的
在大多数面向对象的语言(如 Java 或 C#)中,引用可能为空。在调用任何方法之前,需要检查这些引用以确保它们不为 null,因为通常不能在 null 引用上调用方法。而不是使用空引用来表示对象的缺失(例如,不存在的客户),而是使用实现预期接口但其方法主体为空的对象。与正常工作的默认实现相比,此方法的优点是 Null 对象非常可预测且没有副作用:它不执行任何操作。
解释
真实世界的例子
我们正在从节点构建二叉树。有普通节点和“空”节点。 遍历树通常不会导致错误,因此我们在必要时使用空对象模式。
通俗地说
空对象模式优雅地处理“空”对象。
维基百科说
在面向对象的计算机编程中,空对象是没有引用值或具有定义的中性(“null”)行为的对象。空对象设计模式描述了此类对象的用法及其行为(或缺少行为)。
程序示例
这是接口"Node"的定义。
java
public interface Node {
String getName();
int getTreeSize();
Node getLeft();
Node getRight();
void walk();
}我们有两个"Node"的实现。正常实现"NodeImpl"和空节点"NullNode"。
java
@Slf4j
public class NodeImpl implements Node {
private final String name;
private final Node left;
private final Node right;
/**
* Constructor.
*/
public NodeImpl(String name, Node left, Node right) {
this.name = name;
this.left = left;
this.right = right;
}
@Override
public int getTreeSize() {
return 1 + left.getTreeSize() + right.getTreeSize();
}
@Override
public Node getLeft() {
return left;
}
@Override
public Node getRight() {
return right;
}
@Override
public String getName() {
return name;
}
@Override
public void walk() {
LOGGER.info(name);
if (left.getTreeSize() > 0) {
left.walk();
}
if (right.getTreeSize() > 0) {
right.walk();
}
}
}
public final class NullNode implements Node {
private static final NullNode instance = new NullNode();
private NullNode() {
}
public static NullNode getInstance() {
return instance;
}
@Override
public int getTreeSize() {
return 0;
}
@Override
public Node getLeft() {
return null;
}
@Override
public Node getRight() {
return null;
}
@Override
public String getName() {
return null;
}
@Override
public void walk() {
// Do nothing
}
}然后,我们可以构造和遍历二叉树,而不会出现以下错误。
java
var root = new NodeImpl("1",
new NodeImpl("11",
new NodeImpl("111", NullNode.getInstance(), NullNode.getInstance()),
NullNode.getInstance()
),
new NodeImpl("12",
NullNode.getInstance(),
new NodeImpl("122", NullNode.getInstance(), NullNode.getInstance())
)
);
root.walk();程序输出:
1
11
111
12
122类图

适用性
在以下情况下使用空对象模式
- 您希望避免显式空值检查,并保持算法优雅且易于阅读。