Skip to content

目的

在大多数面向对象的语言(如 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

类图

alt text

适用性

在以下情况下使用空对象模式

  • 您希望避免显式空值检查,并保持算法优雅且易于阅读。

鸣谢