《Effective Ruby:改善Ruby程序的48条建议》一第2条:所有对象的值都可能为nil

简介:

本节书摘来自华章出版社《Effective Ruby:改善Ruby程序的48条建议》一书中的第1章,第1.2节,作者[美]彼得 J.琼斯(Peter J. Jones),更多章节内容可以访问云栖社区“华章计算机”公众号查看

第2条:所有对象的值都可能为nil

运行的Ruby程序中的每个对象都源自同一个类,即以某种方式继承自类BasicObject。试想这一个个相互关联的对象是怎样以BasicObject为根节点构成一个熟悉的属性图的。在实践中,这意味着一个类的对象能够被另一个类的对象替换(感谢多态)。这就是我们能将一个行为类似数组却又不真的是数组的对象传递给一个以Array对象为预期参数的方法的原因。Ruby程序员喜欢称之为“鸭子类型”(duck typing)。与其要求对象是某个给定类的实例,不如将注意力放在该对象能做什么上;换句话说,接口高于类型。用Ruby的术语来说,鸭子类型意味着,相比is_a?方法你更喜欢使用respond_to?方法。

不过实际中很少见到有方法通过使用respond_to?进行参数检查来保证其使用正确的接口。作为替代,我们更倾向于直接调用对象的方法,在对象没有该方法时,让Ruby自己在运行时触发NoMethodError异常。表面上看,这似乎给Ruby程序员带来了真正的问题。好吧,是这样的,只你我二人知道。这也是测试如此重要的原因。没有什么会阻止你意外地将Time类型对象传递给接收Date对象的方法。我们需要通过优秀的测试挑出各种各样的错误。感谢测试,这些类型的问题是可以通过测试避免的。但即使这样,这些多态替换也可能使经过测试的应用程序出现问题:
image

当你调用一个对象的方法而其返回值刚好是讨厌的nil对象时,这种情况就会发生……nil是类NilClass的唯一对象。这样的错误会悄然逃过测试而仅在生产环境下出现:如果一个用户做了些超乎寻常的事情。另一种导致该结果的情况是,当一个方法返回nil并将其作为参数直接传给另一个方法时。事实上存在数量惊人的方式可以将nil意外地引入你运行中的程序。最好的防范方式是:假设任何对象都可以为nil,包括方法参数和调用方法的返回值。
避免在nil对象上调用方法的最简单的方式是使用nil?方法。如果方法接收者(receiver)是nil,该方法将返回真值,否则返回假值。当然,nil对象在Boolean上下文中总是假值,因此if和unless表达式可以如你所期望的那样工作。以下几行代码是等
价的:


249cdfb5f74331da814e375c45dfa57c46df7431

将变量显式转换为期望的类型常常比时刻担心其为nil要容易得多。尤其是在一个方法即使是部分输入为nil时也应该产生结果的时候。Object类定义了几种转换方法,它们能在这种情况下派上用场。比如,to_s方法会将方法接收者转化为string:


9c60fe7004dcd67ce278eec615d8da5128d382fb

如你所见,NilClass#to_s返回一个空字符串。使to_s如此之棒的原因是String#
to_s方法只是简单返回self而不做任何转换和复制。如果一个变量是string,那么调用to_s的开销最小。但如果变量期待string而恰好得到nil,to_s能帮你扭转局面。作为例子,假设一个方法期待其参数之一为string,使用to_s,你可以避免参数为nil产生的
问题:


8639c4fd47aa881fdf0fcbb95d13844b684aef33

有趣的事情还在发生。如你所愿,对几乎所有的内置类(built-in classes)来说都存在一个匹配转换方法。这里有一些适用于nil的最有用的例子。


4445d4fa5aa53a25b1576937cc513147bacf4a80

当需要同时考虑多个值时,你可以使用类Array提供的优雅的讨巧方式。Array#compact方法返回去掉所有nil元素的方法接收者的副本。这在将一组可能为nil的变量组装成string时很常用。比如,如果一个人的名字由f?irst、middle和last组成(其中任何一个都可能为nil),那么你可以用下面的代码组成这个名字:


556e3412715956356a9a2b8de870a97cb0c9b8ac

nil对象的嗜好是在你不经意间偷偷溜进正在运行的程序中。无论它来自用户输入、无约束数据库,还是用nil来表示失败的方法,意味着每个变量都可能为nil。
要点回顾
根据Ruby的类型系统的运作方式,任何对象都可以为nil。
如果方法接收者是nil,nil?方法返回真值,反之为假。
在适合的时候使用转换方法,如to_s和to_i,可以将nil对象强制转换为你期待的类型。
Array#compact方法返回去除所有nil元素的接收者的副本。
相关文章
|
7月前
|
Ruby
|
7月前
|
Ruby
|
4月前
|
开发者 测试技术 Android开发
Xamarin 开发者的五大常见问题及解决方案:从环境搭建到性能优化,全面解析高效跨平台应用开发的技巧与代码实例
【8月更文挑战第31天】Xamarin 开发者常遇问题及解决方案覆盖环境搭建至应用发布全流程,助新手克服技术难关。首先需正确安装配置 Visual Studio 及 Xamarin 支持,设置 iOS/Android 测试环境。利用 Xamarin.Forms 和 XAML 实现高效跨平台开发,共享 UI 和业务逻辑代码。针对性能优化,采取减少 UI 更新、缓存计算结果等措施,复杂问题则借助 Xamarin Profiler 分析。
53 0
|
4月前
|
设计模式 缓存 测试技术
Ruby代理模式之谜:如何用简单的方法创建灵活的对象接口?
【8月更文挑战第31天】代理模式是一种设计模式,通过创建代理对象来控制对目标对象的访问,可在Ruby中通过Proc对象、模块混入等方式实现。本文介绍了代理模式的概念及其实现方法,并提供了缓存、验证和日志代理等应用场景的示例代码,帮助开发者更好地理解和运用这一模式,提升程序灵活性与健壮性。
37 0
|
7月前
|
Ruby
|
7月前
|
Ruby
|
缓存 监控 数据库
使用Ruby构建可扩展的Web应用程序
在当今科技驱动的世界中,Web应用程序成为了企业和个人进行业务活动、提供服务和与用户互动的重要方式。而Ruby作为一种简洁、优雅且易于学习的编程语言,已经成为许多开发者的选择。本篇博客将介绍如何使用Ruby构建可扩展的Web应用程序。
109 0
Ruby Programming | 连载 07 - Ruby 对象的原生行为
Ruby Programming | 连载 07 - Ruby 对象的原生行为
Ruby Programming | 连载 07 - Ruby 对象的原生行为
|
存储 IDE 开发工具
Ruby Programming | 连载 03 - Ruby 对象基础
Ruby Programming | 连载 03 - Ruby 对象基础
Ruby Programming | 连载 03 - Ruby 对象基础
|
定位技术 API 图形学
【Unity开发实战】—— 2D项目1 - Ruby‘s Adventure 游戏世界中各个对象的交互(3-1)
【Unity开发实战】—— 2D项目1 - Ruby‘s Adventure 游戏世界中各个对象的交互(3-1)
296 0
【Unity开发实战】—— 2D项目1 - Ruby‘s Adventure 游戏世界中各个对象的交互(3-1)