1.11 空对象模式
空对象模式(Null Object Pattern) 使用了特殊的放置标记对象来表示空对象。一般来说,如果有一个空引用,则不能调用 reference.field
或 reference.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
这样的表达式,当 salary
、 job
或 p
为空时,表达式则为空。为了避免出现 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论