返回介绍

1.11 空对象模式

发布于 2025-01-04 00:44:55 字数 3304 浏览 0 评论 0 收藏 0

空对象模式(Null Object Pattern) 使用了特殊的放置标记对象来表示空对象。一般来说,如果有一个空引用,则不能调用 reference.fieldreference.method() ,会收到可怕的 NullPointerException 异常。空对象模式使用特殊的对象来表示空对象,以避免使用实际的 null ,从而可以调用空对象上的字段和方法引用。空对象的使用结果在字面上应等同于“什么都不做”。

1.11.1 简单范例

假设有一个这样的系统:

class Job {
  def salary
}

class Person {
  def name
  def Job job
}

def people = [
  new Person(name: 'Tom', job: new Job(salary: 1000)),
  new Person(name: 'Dick', job: new Job(salary: 1200)),
]

def biggestSalary = people.collect { p -> p.job.salary }.max()
println biggestSalary

运行后,打印结果为 1200 ,假设现在像下面这样调用:

people << new Person(name: 'Harry')

如果再次计算 biggestSalary 就会得到空指针异常。

为了解决这个问题,引入一个 NullJob 类,改变上面语句为:

class NullJob extends Job { def salary = 0 }

people << new Person(name: 'Harry', job: new NullJob())
biggestSalary = people.collect { p -> p.job.salary }.max()
println biggestSalary

这实现了我们的期望目标,但却不是 Groovy 的最佳方式。利用 Groovy 的安全解除引用操作符( ?. )和对空值敏感的一些闭包,往往不必去创建特殊的空对象或空类。下面就是对上述范例采取更为 Groovy 方式的做法:

people << new Person(name:'Harry')
biggestSalary = people.collect { p -> p.job?.salary }.max()
println biggestSalary

这里要注意两件事。首先, max() 对空值敏感,所以 [300, null, 400].max() == 400. 。其次,利用 ?. 操作符,对于 p?.job?.salary 这样的表达式,当 salaryjobp 为空时,表达式则为空。为了避免出现 NullPointerException 异常,不必编写复杂、层层内嵌的 if...then...else 语句。

1.11.2 树范例

下面范例展示了如何在树形结构中,计算所有数值的大小、累计加和及累计乘积。

首先在计算方法中引入处理空值的逻辑。

class NullHandlingTree {
  def left, right, value

  def size() {
    1 + (left ? left.size() : 0) + (right ? right.size() : 0)
  }

  def sum() {
     value + (left ? left.sum() : 0) + (right ? right.sum() : 0)
  }

  def product() {
     value * (left ? left.product() : 1) * (right ? right.product() : 1)
  }
}

def root = new NullHandlingTree(
  value: 2,
  left: new NullHandlingTree(
    value: 3,
    right: new NullHandlingTree(value: 4),
    left: new NullHandlingTree(value: 5)
  )
)

println root.size()
println root.sum()
println root.product()  

如果引入空对象模式(定义 NullTree 对象),那么就可以简化 size()sum() 以及 product() 方法中的逻辑,这些方法从而就能更为清晰地展现通常情况下的逻辑。

class Tree {
  def left = new NullTree(), right = new NullTree(), value

  def size() {
    1 + left.size() + right.size()
  }

  def sum() {
     value + left.sum() + right.sum()
  }

  def product() {
     value * left.product() * right.product()
  }
}

class NullTree {
  def size() { 0 }
  def sum() { 0 }
  def product() { 1 }
}

def root = new Tree(
  value: 2,
  left: new Tree(
    value: 3,
    right: new Tree(value: 4),
    left: new Tree(value: 5)
  )
)

println root.size()
println root.sum()
println root.product()

范例的运行结果是:

4
14
120  

注意:空对象模式有个特殊变体,那就是将它与单例模式组合使用的情况。因此,在需要像上例中的那个空对象时,我们不能编写新的 NullTree() ,而是应该用一个必要时放在数据结构中的空对象实例。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文