聊聊 Ruby on Rails
,一个语言的问世,它是为一个特定场景设计的,一定有它最实用的场景。所以我们心知肚明在有 些应用中 Ruby 是有它的绝对优势的。另外,语言也是一个工具,它的性能能发挥到什么程度,和使用者的技术水平也是有很大关系的。我不想说喜欢吐槽 Ruby 的人都是 Ruby 写的不好的。
程序员装逼语录二则:
“ Php 是世界上最好的语言。 ”
“ Ruby 写出来的程序就是一坨。”
先不跟你争论着两条语录有多少人真的理解,又有多少人相信。先和大家分享一条我很喜欢的挪威的格言:
“There’s no such thing as bad weather, only bad clothing.”
翻译过来就是:“不要抱怨天气不好,只是你没穿对衣服。”
当 然,这句话也是有槽点的,看官请三缄其口。想表达的一个意思是,一个语言的问世,它是为一个特定场景设计的,一定有它最实用的场景。所以我们心知肚明在有 些应用中 Ruby 是有它的绝对优势的。另外,语言也是一个工具,它的性能能发挥到什么程度,和使用者的技术水平也是有很大关系的。我不想说喜欢吐槽 Ruby 的人都是 Ruby 写的不好的。因为 Ruby 确实有很多局限性,我们后面会聊。但是作为一门语言,我觉得 Ruby 还是很值得掌握和学习的。
先说说学好 Ruby 有什么好处吧。
Ruby on Rails 的最大优势我觉得有四个:
- 语言灵巧轻便,适合快速开发和部署。
- 各种本身自带的 “magic” 以及可以通过 gem 或者 vendor code 加载的 magic。让代码美观简洁。
- 很容易与各种前端 JS 框架集成(包括Backbone,Ember,React等等)
- 有一个 Rails console 可以让 test 和 debug 变得异常轻松。
如 果你到湾区的各个中小公司走走看看,很大一部分最原始的第一个代码库都是基于 Ruby on Rails 的。尽管后期会有各种拆分和重写,对原来的 Ruby 代码能够很好的把握也是一项极为重要的技能。我早年是写 C++ 和 Python 的,最近三年多,却是有百分之六七十的时间写的是 Ruby,偶尔写写 Java 和 Scala,但是 Ruby 依然是我写的最顺手的语言,没有之一。因为 Square 和 Airbnb 两家公司 Ruby 都用的很多,所以对 Ruby 熟练也成为我在工作上的一个小小的优势。
虽然很喜欢 Ruby,但是却除了上面四点不想写太多废话来解释 Ruby 为什么好。不如说点有用的,了解并如何尽可能的避免 Ruby on Rails 里面的坑。
Ruby on Rails 最大的问题可能就是这个语言写代码开发起来快,维护起来却很困难。什么原因呢?怎么避免呢?
首先,Ruby 是动态类型的。什么意思大家都懂,就是一个变量没有类型声明,run time 的时候给它什么值它就是什么类型。语言的官方说法叫做 duck typing:
“ If an object walks like a duck and talks like a duck, then the Ruby interpreter is happy to treat it as if it were a duck. ”
“ 如果一个对象和鸭子一样走路一样说话,那么Ruby就很欣然地认为这就是一只鸭子。”
没 有类型申明的代码写起来简洁方便,尤其是不同对象间有可继承重用的关系的时候,如果换了 Java 这样的强类型语言就必须使用 interface 或者 abstracted class 来严格按照 design pattern 来处理。而在 Ruby 里面,你只要心中想要给一个变量什么值,那么只要后面赋值正确,这个变量就可以有什么值。这就必然引来两个麻烦:
- 不小心赋错值,Ruby 依然将计就计,也不抱怨。只不过在实际运行中让你找不着北,得到一个完全出乎意料的结果。
- 需 要 refactor 代码,也就是代码重构的时候。这几乎可以说是一场灾难。试想下你有一百把钥匙一百把锁,有类型申明的语言可能对于不同型号的钥匙和锁有所区分,而无类型申 明的语言所有的钥匙和锁几乎都长的一样。代码重构就好像把所有配对打乱,又要以新的形式组合。最后结果就是看着重构后利索无比的新代码,一不小心就给你 exception 了。
一个很有效的解决办法是,对一些重要的,尤其是 API 的参数类型进行强制类型检查和断言。比如在 Airbnb,我们会用一个 validation gem 对所有需要的变量去用代码来检测其类型,类似于 “validate count is_a Integer” 这样的断言。
第二,很多人对 Ruby on Rails 的 ActiveRecord 一知半解。ActiveRecord 可能是 Rails 里最强大的 magic 之一(或者没有之一)了。ActiveRecord 是对代码中数据库访问和业务逻辑访问的一个混合体。如果写的好,可以事半功倍,写出 DB 访问效率高且干净利落的数据库访问相关的代码,比如它的 scope 和 association 的运用。然而,如果对于 ActiveRecord 怎么实现似懂非懂,加上对 MySQL 的 execution plan 一知半解,很可能写出的代码一到 production 就会各种崩溃。举三个常见的例子:
- 老实说,我觉得不知道 N+1 Query 是啥的程序员都应该好好去了解下。N+1 Query 几乎是我见过的引发 production 问题的最常见的错误之一(也许没有之一)了。
- Rails 的association。最便利的 wrap 对象间 1对1、1对多、多对多等关联的 magic。很多别的语言应该都没有这个概念。Rails 的官方文档上有,强烈推荐深入了解。这也是平常见到的被错用最多的 magic 之一了。
- Rails 中的一些 job 需要在数据库中找到满足某些条件的 records 并进行处理。Rails 常用的有 find、find each、find in batch 等等,虽然不一样的查询语句都能给你一样的结果,但是因着他们对应的 SQL 语句的转化不一样,很多的时候效率天差地别。这也是 Ruby 代码中可以看出一个程序员对 Rails 的了解的一个常见的点。
例子还很多,坐下来可以慢慢写,这里也只是顺手写几个最先想到的。所以我们也不用一边写着低效的 Ruby 代码一边骂 Ruby 这不好那不好了。我琢磨 Ruby 可能觉得更怨。
第三,因着 Rails 中这样那样的 magic,写好 Ruby 的 RSpec 测试例可能是一件比写好 Ruby 代码还难的事了。写好 RSpec 其实有很多的技巧,最常用的:
- 各种 stubbing,包括对 FactoryGirl 和 Fixture 等的应用,好的 Rspec 其实可以帮助避免很多 Rails 里面的坑。怎么写好 Rspec,这个也不是一篇能说清的,有兴趣的可以留言,以后我如果有好的资料可以分享给大家。
- 这一条也适用于所有的程序语言的测试例的编写。是自上而下还是自下而上,怎么保证覆盖率,先写代码还是先写 test,很多好书。别的语言的写 test 的思想到这里也是适用的。
老实说我学写 RSpec 可能当初花的时间和写 Rails 代码本身花的时间一样多。即使是现在,我也不敢说我能把 RSpec 写到很好,只能说我愿意花时间精力努力把 spec 写好罢了。
第四,Rails 本身已经是一门语法极其精炼的语言了,没有必要为了把代码变得更紧凑而使用一些很炫的偏僻的语法。记 得我刚开始写 Ruby 的时候,喜欢干的一件事就是把整个函数我用一行 Ruby 各种技巧就写出来了。然后呢?不仅可读性差,有的时候时间长了,自己都忘了是怎么玩的了。后来越写越老实,怎么容易懂就怎么写。有一个写程序的原则我觉得 实用所有的语言:好的程序是不应该需要太多注释的。这包括结构的清晰和最合适的命名。这个原则对 Ruby 尤其重要,因为 Rails 本身已经各种 magic,很容易把代码写的出神入化,然后可读性极差。尤其是即使如 RubyMine 这样的 IDE 在跳转到函数定义或函数应用的这一功能上也不可能做到准确,因此可读性差的代码几乎注定早晚就成了个坑。所以当你写了一段 Ruby 代码你觉得有必要加一段注释来说明你想干什么的时候,可能应该先想想是不是应该重写这一段代码了。
其实还有第五第六第七……, 因为一门语言可以聊的东西太多了。记得以前 Square 的时候每周都有 Rails 的专门的研讨班,每周聊都聊不完,何况一篇文章?Square 两年于我最大的收获也是学会写 Ruby,记得我还曾半开玩笑的和朋友说过:“工作做的越久,越是什么技能都不敢往简历上写了,因为不断见到真的牛人。以后我找工作,简历上只写我会写一 点 Ruby 代码吧。” 因为很喜欢 Ruby,我的微信号都取了在 Mac 上写 Ruby 代码的含义。当然,Java 和 Scala 我也很喜欢。
发表回复