Ruby 如何看待 Ruby 中的 “NULL”

qingxp9 · 2015年11月01日 · 最后由 ywencn 回复于 2015年11月01日 · 2552 次阅读

偶然看到一篇文章:《“NULL”:计算机科学中的最严重错误,造成十亿美元损失》

主要内容是说明 NULL 是计算机科学中最糟糕的错误,并引用了图灵奖得主 Tony Hoare 的话

“我把 Null 引用称为自己的十亿美元错误。它的发明是在 1965 年,那时我用一个面向对象语言 ( ALGOL W ) 设计了第一个全面的引用类型系统。我的目的是确保所有引用的使用都是绝对安全的,编译器会自动进行检查。但是我未能抵御住诱惑,加入了 Null 引用,仅仅是因为实现起来非常容易。它导致了数不清的错误、漏洞和系统崩溃,可能在之后 40 年中造成了十亿美元的损失。近年来,大家开始使用各种程序分析程序,比如微软的 PREfix 和 PREfast 来检查引用,如果存在为非 Null 的风险时就提出警告。更新的程序设计语言比如 Spec# 已经引入了非 Null 引用的声明。这正是我在 1965 年拒绝的解决方案。” 《Null References: The Billion Dollar Mistake》托尼·霍尔(Tony Hoare)

文章通过这几个观点逐步进行了阐述:

  1. 颠覆类型
  2. 是凌乱的
  3. 是一个特例
  4. 使 API 变得糟糕
  5. 使错误的语言决策更加恶化
  6. 难以调试
  7. 是不可组合的

其中”API 变得糟糕“以 Ruby 举了个例子,

以下原文引用:

假设我们创建一个 Ruby 类充当一个键值存储。这可能是一个缓存、一个用于键值数据库的接口等等。我们将会创建简单通用的 API:

class Store
         # associate key with value
         def set(key, value)
             ...
         end

         # get value associated with key, or return nil if there is no sch key
         def get(key)
             ...
         end 
    end

我们可以想象在很多语言中类似的类(Python、JavaScript、Java、C# 等)。 现在假设我们的程序有一个慢的或者占用大量资源的方法,来找到某个人的电话号码——可能通过连通一个网络服务。

为了提高性能,我们将会使用本地存储作为缓存,将一个人名映射到他的电话号码上。

store = Store.new()
store.set('Bob', '801-555-5555')
store.get('Bob') # returns '801-555-5555', which is Bob’s number
store.get('Alice') # returns nil, since it does not have Alice

然而,一些人没有电话号码(即他们的电话号码是 nil)。我们仍然会缓存那些信息,所以我们不需要在后面重新填充那些信息。

store = Store.new()
store.set('Ted', nil) # Ted has no phone number
store.get('Ted') # returns nil, since Ted does not have a phone number

但是现在意味着我们的结果模棱两可!它可能表示:

  1. 这个人不存在于缓存中(Alice)
  2. 这个人存在于缓存中,但是没有电话号码(Tom)

一种情形要求昂贵的重新计算,另一种需要即时的答复。但是我们的代码不够精密来区分这两种情况。 在实际的代码中,像这样的情况经常会以复杂且不易察觉的方式出现。因此,简单通用的 API 可以马上变成特例,迷惑了 null 凌乱行为的来源。 用一个 contains() 方法来修补 Store 类可能会有帮助。但是这引入重复的查找,导致降低性能和竞争条件。

(引用完)

大家如何看待这个问题

你这个缓存的结构设计得不太好,应该把整个人这个对象存起来 所以如果 store.get("Bob") 的时候为 nil 了,就是这个人不存在 如果是 store.get("Bob").tel 为 nil 了,就是电话号码不存在

需要 登录 后方可回复, 如果你还没有账号请 注册新账号