「译」为什么 null 不好?

  • 原文地址:why null is bad跳转中...
  • 原文作者:Yegor Bugayenko
  • 译者:高老庄里的猿
先来看个 JAVA 中使用 null 作为返回值的简单例子:
「译」为什么 null 不好?

文章插图
 
该方法最大的问题是返回 null 代替了对象 。在面向对象规范中使用 null 是个非常糟糕的做法,应该极力避免 。有很多论据可以支持这一观点,包括 Tony Hoare 的演讲《Null References, The Billion Dollar Mistake》和 David West 的《Object Thinking》 整本书 。接下来我将所有的论据做一些整理并使用合适的面向对象结构代替 null 作为返回值 。目前看来,至少有两种方法可以代替使用 null。
1、使用空对象设计模式(最好定义一个常量)
「译」为什么 null 不好?

文章插图
 
2、当不能返回一个对象时,可以抛出异常来让调用方 fail-fast(快速失败)
「译」为什么 null 不好?

文章插图
 
现在来看看反对使用 null 的依据,除了上面提到的 Tony Hoares 的演讲和 David West 的书籍,还有 Robert Martin 的 《Clean Code》、 Steve McConnell 的《Code Complete》、John Sonmez 的 《Say “No” to “Null”》以及 StackOverflow 上的讨论《Is returning null bad design? 》,这些我在写这篇文章之前都看过 。
特殊错误处理每次将对象引用作为输入时都必须检查它是 null 的还是有效的,如果忘了检查,将会导致运行时 NPE(Null Pointer Exception) 。因此,你的代码逻辑会被多种检查和 if/then/else 分支所污染 。看看下面例子:
「译」为什么 null 不好?

文章插图
 
这是 c 语言和其他很多面向过程编程语言处理异常所使用的方法,面向对象编程引入异常处理主要就是为了消除这些特殊的错误处理逻辑 。在面向对象编程中,我们将异常以冒泡的方式不断的向上抛出直到应用层,这样我们的代码将变得更加小而美 。
dept.getByName("Jeffrey").transferTo(dept2);null 是面向过程编程的"封建余孽",请使用 null 对象或者异常代替之 。
语义的二义性为了显示的将"函数会返回真实的对象或者 null "这层含义表达出来,getByName() 必须命名为getByNameOrNullIfNotFound() 。每个类似的函数都应该这样做,否则会给代码阅读者来带来歧义 。
为了语义的准确性,你应该为函数定义更长的名称 。
为了消除歧义,函数尽量返回一个真实的对象、一个 null 对象或者抛出一个异常 。
有些人会争辩说有时为了性能,不得不返回 null 。比如 java Map 接口中的 get() 方法,当在 map 中找不到相应的条目时会返回 null,例如:
「译」为什么 null 不好?

文章插图
 
由于 Map 的 get() 方法返回 null,上面代码只会在 map 中搜索一次 。如果我们想重写 Map 的 get() 方法以让其在查找不到条目时抛出异常,代码应该这样写:
「译」为什么 null 不好?

文章插图
 
很明显,这个方法比第一个方法慢两倍,怎么办呢? 我觉得 Map 的接口设计存在缺陷(无意冒犯作者),它应该返回一个迭代器 Iterator 以便让我们代码可以像如下这样:
「译」为什么 null 不好?

文章插图
 
BTW,这正是 C++ 标准库(STL)中 map::find() 方法的设计思路 。
计算机思维 vs 对象思维假如某人知道 Java 对象是一个指向某个数据结构的指针,并且 知道 null 是一个空指针(在英特尔 x86 处理器中等于 0x00000000),那他应该能接收 if(employee == null) 这个语句 。但是,如果以对象思维来进行思考,这个语句就没意义了 。从一个对象的角度来看,我们的代码是这样的:
  • Hello, 请问是软件部吗?
  • 是的 。
  • 麻烦让我和你们的 employee(员工) Jeffrey 聊聊 。
  • 请稍等...
  • Hello
  • 你是 NULL ?
上面对话的最后一句看起来很奇怪,不是吗? 相反,如果他们在接到我想与 Jeffrey 进行通话的需求后直接挂断电话会快速给我们制造个故障(异常) 。这时我们可以尝试着再次拨过去或者直接告诉我们的主管无法联系到 Jeffrey 来完成更大的交易 。
【「译」为什么 null 不好?】或者,他们可以让我们与另一个人交谈,他不是 Jeffrey,但如果我们需要“特定的” Jeffrey(null 对象)的话,他可以帮助我们解决大多数问题,也可以拒绝帮忙 。


推荐阅读