Ruby Ruby 和元编程的故事 - 第 0 回: 欲练神功,必先自废武功

hisea · 2012年02月13日 · 最后由 zw963 回复于 2012年02月28日 · 10163 次阅读

http://hisea.me/p/ruby-story-ep0

开篇

随着 Ruby/Rails 的逐渐火热,越来越多的开发人员转向 Ruby.
Rails 简单易用,很多其他语言开发人员很快就做出简单的 Web App.
然而另一方面,Ruby 做为一个语言,确需要一些学习和研究才能掌握其方方面面。
本系列就是专注于提供一些关于 Ruby 语言和其元编程技巧的文章。 在编写这个系列文章的时候,Hisea 对读者做了以下的一些假设:

  • 已经熟悉用某种程序些程序
  • 已经熟悉 Ruby
  • 已经能够假设 Ruby 环境并可以运行代码

Ruby 是个神马语言

Ruby 是一个开源的动态类型,强类型,动态的,面向对象的解释型语言。

看上去貌似很麻烦,这些形容词拿出来一个一个解释就很简单了。

  • 开源的:
    Ruby 开源协议 1.9.3 以前是 GPLv2 和 Ruby, 1.9.3 之后是 Simplfied BSD 和 Ruby 协议

  • 动态类型:
    类型的检查是运行时进行的,相对于 Java 等静态类型语言,类型检查是在编译时进行的

  • 强类型语言:
    不同数据类型在操作和运算中出现混用时,除非显示转换类型,否则会报类型错误,相对于 JavaScript 等弱类型语言,不存在类似的限制。

  • 动态语言: 动态语言的定义还很模糊,很多人把动态语言跟动态类型语言搞混,对于 Hisea 来说,动态语言就是类,方法,及其他定义可以在运行时进行改变,元编程就是利用这一特性。由此可见,动态语言跟动态类型语言并不是一个概念。

  • 面向对象:
    不解释

  • 解释型: 区别于编译型语言。

欲练盖世神功,必先自废武功

Ruby 的动态特性跟以往的静态类型语言相比,需要很多思维上的转换。
如果带着以往的静态语言思维来学习 Ruby,往往需要走些弯路。
对于有其他语言基础的初学者,有些已经练好的武功最好一开始彻底忘掉,才能更好的练成盖世 Ruby 神功。

下面是一些学习 Ruby 需要深刻在心的概念,很多可能跟你现有的思维定式相违背。最好的办法就是清空现有想法,不带偏见的从 0 开始学习 Ruby.

0.万物皆是对象

Ruby 里的一切,一切一切,无论是色还是空,都是对象。 不象 Java,Ruby 没有原始类型。1 是对象,1.2 是对象,nil 是对象,true/false 是对象。 类是对象,module 也是对象。

1.拥抱动态类型

Ruby 界有条谚语,如果你扭的像个鸭子,叫的像个鸭子,你就是个鸭子。
这是 Ruby Duck Typing 的特性。看上去很简单,但是真正把这句话融入到骨髓却不是件易事。如果你在代码中有很多检查类型的判断,例如 is_a, kind_of 等方法,甚至太多的 nil 检测,都是没有很好的理解动态类型的特征。

2.忘记静态的类设计

这一条其实是第一条的延续。很多有多年Java或者C++/C#经验的开发人员(包括Hisea自己),在设计复杂的系统时,都会很自然的想到继承和接口(Interface).在Ruby中设计继承时需要认真的思考一下几个问题。

  1. 父类在做什么? 例如很多人喜欢这么做:
class 飞行器  
  def 起飞  
    fail "子类包含具体的实现!"   
  end  
end  

class 飞机 < 飞行器  
  def 起飞  
    #飞机起飞实现 
  end  
end  

class 热气球 < 飞行器  
  def 起飞  
    #热气球起飞实现  
  end  
end  

但看以上的例子,父类的作用仅仅是定义了 API 的存在,这在 Java 中是非常常见的,可是在 Ruby 中,这个父类其实存在的意义不是特别大。Ruby 更倾向于,任何能起飞 (拥有‘起飞’方法) 的对象都是飞行器,而不是任何继承飞行器的类的对象。

  1. 需要用继承分享代码么? ActiveRecord 需要你继承 ActiveRecord::Base,例如:
class Post < ActiveRecord::Base  
end

Hisea 本人觉得类似 Mongoid 的 Mixin 方式更适合解决类似的问题。Post 从设计逻辑上来讲,跟 ActiveRecord::Base 没有半毛钱关系。继承关系完全用于分享代码,而在 Ruby 中,更好的分享代码的办法是用 Mixin.
比如第一个例子中,我们可以定义一个飞机的 module,实现 起飞 方法,任何 include 起飞的类,都是某一种飞机。

3.让动态进行到底

Ruby 不仅仅是类型是动态的,还有很多其他的也很动态,比如可以动态的 include 或者 extend 一个 module,可以动态的定义或者重新定义一个类,或者一个方法。这在 Java 等静态语言来看是非常的无组织无纪律极其自由散漫。Hisea 面试 Ruby 程序员时经常会问到关于 Metaprogramming 的问题,其实对 Ruby 元编程这个特性懂多懂少并无所谓,只是拿来刺探 Ruby 经验多少的一个问题。资历尚浅的 Ruby 程序员对于元编程总会抱有这样那样的保守想法。

4,忘记 UML(或其他) 设计大法

这个话题是 3 的继续,公司新招了一个 Ruby 程序员,他来了一两个星期后问了个问题,问为什么公司 (或者其他 Ruby 程序员) 不爱用 UML 之类的设计工具,当时我也一时找不出答案,后来开车回家的路上用半个小时想明白了,UML 是一个类只见关系的静态表示,Ruby 运行时的情况却是不停在变幻。用静态去表示动态,自然捉襟见肘。

5.为什么不用 IDE?

很多从 Java 阵营转来,尤其是有多年 eclipse 经验的 Ruby 初学者尤其是经常爱问,Ruby 用什么 IDE。
得到的回答往往是 text mate,vim,sublime text 2 等等文本编辑器。
很多人可能纳闷,为什么 Ruby/Rails 没有一个 IDE 占领大片江山的情况,为什么 Netbeans/Eclipse 再 Ruby 开发阵营中没有其他语言开发占的地位重要。

其实答案很简单。

  1. IDE 最讨喜的功能是什么?
    很多用惯了 IDE 开发的 Java 程序员甚至 XCode 程序员,都会说最爱的功能是代码不全,object 之后按一下'.'立马生成一个方法列表。转到 Ruby 用文本编辑器,没有这个功能,很是郁闷。其实道理很简单,如果方法都是动态生成的,在写程序的时候怎么能给出一个列表呢。

  2. Debugger 还是必要工具么?
    静态语言开发,调试是居家旅行杀人灭口必备良药。而 IDE 又是调试的好帮手。 Ruby/Rails 的 Debugger 还没那么成熟,而且如果用 logger, raise 在适当的地方输出 inspect, to_yaml 等内容,也可以很容易的找到错误。

  3. Eclipse 可以很好的配置 Java 开发环境。 Ruby/Rails有好用的RVM,RubyGems,Bundler.

所以,不补全,不调试,不配环境,要 IDE 不也是拿来当文本编辑器用么。

本回完

零零总总的想了些 Ruby 的主要特性,以及常见的初学者的疑问/误区。
本文打算从这里开始,写点 Ruby 深层次的一些概念和特性,并且怎么利用这些特性进行 Metaprogramming.

且听下回分解

下回注重一些对象 Object 的故事。

联系作者

如果你有任何问题,欢迎讨论。

作者:Hisea
web: http://hisea.me
email: [email protected]
weibo: http://www.weibo.com/zyinghai
twitter: https://twitter.com/zyinghai
github: https://github.com/hisea

Hisea.me 版权所有

支持技术贴~ 这个坛子里的技术贴太少了 :>

写得太好了,读后感觉很受用,非常感谢。

期待下回分解。似乎正文可以 format 一下。

ide 用来追踪代码貌似还比较好吧。。。

ruby1.9.3 上的 debugger 有点问题

不必自废武功。。之前就没练过别的

写得很不错!

期待下篇。

期待下篇 + 1

@DavidWei 这个只是个临时的解决方案,之前试过了,开始管用,后来怎么就不灵了!奇怪!

@huacnlee 不会不能渲染 md 把。华顺做上去啊。。。

@hisea 我感觉 ruby 最难的是元编程,其实 ruby 用了一些函数式编程的思想。学了学 SCIP??,感觉这个难啊。是语言设计的一个坑。不过目前我去研究编译器去了,真好玩。。

感谢楼主

期待下篇 +214 可以么。。。

写的很好 “如果你在代码中有很多检查类型的判断,例如 is_a, kind_of 等方法,甚至太多的 nil 检测,都是没有很好的理解动态类型的特征”非常有道理。 “们可以定义一个飞机的 module,实现 起飞 方法,任何 include 起飞的类,都是某一种飞机。”这句说的就是“代码”中的 组合优先于继承。 我觉得 元编程 难的主要原因是不知道在哪种场合使用,会不会把问题搞的更复杂了。

#15 楼 @jinleileiking ruby 有一些对函数式语言的支持,比如把方法封装成 block,然后传到另外一个方法里面去,不过函数不是 Ruby 第一等公民,还是跟传统的函数式语言有一定的差别。

#18 楼 @yakjuly 是的,即便学了很多很多元编程技巧,能融会贯通综合起来实用还是有一定挑战的。不过有些简单的用途,其实很多已经成为范式。再些 Gem 里面用的还是很广泛的。

== Hisea 面试 Ruby 程序员时经常会问到关于 Metaprogramming 的问题 ==

晕死,为什么我在广州面试了好多个,怎么没有一个问我 Ruby 最基本的的问题? 要么就是招我去做运维 (可是我又不会所谓的 Mysql 数据库调优) 要么,Ruby 只字不提,先来一句:使用 Rails 做过什么项目?仿佛 Rails 跟 Ruby 根本没有任何关系一样,从头到尾,不提 Ruby 一个字。真郁闷!

== 得到的回答往往是 text mate,vim,sublime text 2 等等文本编辑器。==

不要忘记 Emacs 嘛。而且 Ruby 调试器 ruby-debug 也算很成熟啦,Emacs 甚至专门 有一个插件叫做 dbgr, 支持 linux 下几乎所有的调试器,针对不同的模式,自 动调用对应的调试器,提供大教堂式的调试界面. 下面是 dbgr 的 github. https://github.com/rocky/emacs-dbgr

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