Ruby 101:类和对象
Written by Allen Lee
今天开始Ruby ~
虽然仅仅阅读文章也能了解Ruby的语法,但这样就会少很多乐趣,如果你有兴趣学习Ruby,我建议你还是动手试试,与纯粹阅读相比,亲身体验将会有另一番不同的感受。
工欲善其事,必先利其器,想要体验Ruby,就得花点时间配置它的运行环境了。目前可以选择的有Ruby、IronRuby和JRuby,你可以根据自己的喜好/需要选择其中一个,其中,IronRuby需要Microsoft .NET或者Mono的支持,而JRuby则需要JVM的支持。
Ruby运行环境的安装和配置非常简单,到官网下载最新版的压缩包,把它解压到C:\(或者你喜欢的其他地方),然后把C:\Ruby\bin(或者你所选择的路径)添加到Path环境变量,这样就好了!(这是Windows平台的安装方法,如果是Ubuntu,可以通过Package Manager直接安装。)对于IronRuby,如果你已经安装了Visual Studio 2005/2008,那么你只需从codeplex.com/ironruby上下载最新版的压缩包,把它解压到你喜欢的地方,然后把bin的路径添加到Path就可以了。对于JRuby,除了上述方法之外,你还可以下载并安装NetBeans的Ruby专用版,它已经自带JRuby 1.2.0,当然,如果你想要最新的1.3.1,那就要自己动手了。
接下来,打开运行对话框,输入irb,然后按下回车,Ruby的命令行运行环境将会打开,在里面敲下puts "Start Ruby today ~"并按下回车,这将会向控制台输出"Start Ruby today ~":

图 1
puts是一个方法,它负责把数据输出到控制台,在Ruby里,调用方法时通常都是可以省略括号的,当然,如果你已经习惯带括号的调用风格,你也可以把它加上。恭喜你成功运行了你的第一份Ruby代码!
模块、类和对象
在Ruby里,创建类型是非常简单的,假如我要创建一个Book类,我可以这样做:

代码 1
接下来,我们创建一个Book的实例对象:

代码 2
b是一个变量,在Ruby里,使用变量时无需声明它的类型,事实上,变量不和类型关联,你完全可以在后面把它用于其他类型的对象(第二个b下面的波浪线是NetBeans提示变量未被使用):

代码 3
而这在静态语言里面是不允许的。回到代码2,我们用new方法创建一个Book的实例对象,但这个new方法从哪里来的呢?它是Ruby的解析器为我们自动创建的默认构造函数吗?不是的,不过,它确实是构造函数。糊涂啦?不要紧,我们会在后面详细探讨,现在你只要记住可以用它来创建对象就行了。
一般情况下,我们会把类组织到不同的命名空间里,在Ruby里,我们用模块来实现命名空间的功能,比如说,我想把Book类放在Ruby101模块里,我可以这样做:

代码 4
当我们把类放进模块,我们就需要通过模块的名字来引用类了:

代码 5
实例成员
现在的Book类空空如也,我想没有人会想要这样一个没用的类,如果想用它来存储一些有用的信息,我们就需要为它创建一些实例字段。
下面,我将会为Book类创建一个initialize方法,用来初始化Book的实例字段:

代码 6
在Ruby里,方法的命名方式也是有规定的,它可以包含字母、数字和下划线(少数特殊字符也允许出现在方法名字里,比如!、?和=,但它们的使用通常有一些约定俗成的意义),并以小写字母或者下划线开头。代码6里的构造函数接受4个参数,这些参数都没有声明类型,这和变量一样,构造函数里分别初始化4个实例字段。字段?慢着!我们还没有定义……
在Ruby里,实例字段都是以@开头的,并且在使用之前无需事先声明,但这些实例字段都是私有的,要从外面访问这些字段,我们需要为它们创建访问器:

代码 7
代码7示范了3种不同风格的访问器,你可以根据个人喜好选择其中一种,一般情况下,我们会采用第三种风格,事实上,第三种风格最终会被"还原"成第一种风格,在某种程度上,你可以把它理解成C# 3.0的自动属性,除非你要向访问器添加(验证)逻辑,否则你没有必要直接使用第一种风格。在Ruby里,方法的最后一个表达式的值将会自动作为方法的返回值,当然,你也可以显式使用return来返回。attr_accessor实际上是一个方法,它的职责就是根据我传给它的名字生成第一种风格的代码,:price是我传给它的参数,:XXX用来表示Ruby的Symbol类型的对象,表面上它和字符串很像,但内里却有着很大不同,就目前而言,你只需把它们之间的区别理解为相同内容的Symbol对象在内存里只存在一份,而相同内容的String在内存里则会存在多份(如果你有兴趣深入了解Symbol,可以阅读Eric Kidd的《13 Ways of Looking at a Ruby Symbol》),和attr_accessor对应的还有attr_reader和attr_writer,顾名思义,它们分别用来创建只读和只写访问器。
下面,我们来看看这些访问器分别是如何使用的:

代码 8
正如你所看到的"title = XXX"实际上会被解析成对title=方法的调用,当然,你也可以使它变得更明显些:b.title=("The Ruby Programming Language")。在调用set_authors方法时,我向它传递一个数组,在Ruby里,数组表示为[XXX, YYY, ZZZ]。
现在,我们统一使用第三种风格为所有实例字段创建访问器:

代码 9
下面,我们创建一个Book的实例对象,并输出它的信息:

代码 10
如果你和我一样都是使用NetBeans,那么你只需要在编辑器任何地方右击鼠标,然后选择Run File就可以运行Ruby代码了:

图 2
运行结果将会显示在Output窗口里:

图 3
在NetBeans里,你可以通过File ->Project Properties轻松切换Ruby的运行环境:

图 4
当然,你也可以使用记事本或者其他你喜欢的文本编辑器来编写Ruby代码,然后把代码保存为XXX.rb文件,并通过命令行用Ruby解析器执行代码,下面分别使用Ruby和IronRuby来执行上面的代码:

图 5
如果你嫌代码9还不够简化,那么你可以试试Ruby的Struct:

代码 11
代码11和代码9是等效的,就像attr_accessor方法那样,Struct最终会帮你生成代码9里的Book类,而用法上和之前是一样的,如果你想创建的是仅用于承载数据的类,那么Struct就是为你量身定做的了。如果将来你想扩展Book类,比如说,为它添加方法怎么办?没问题,既然Struct为你创建的是一个类,那么你可以通过继承扩展这个类,此外,你还可以"直接"扩展这个类。什么意思呢?Ruby允许你修改任何类的定义,即使是内置的类,所以,你可以在代码11后面"重新打开"Book类,并往里面添加方法:

代码 12
由于tags是一个数组(确切地说,应该是tags方法的返回值是一个数组,虽然这只是我们的期望,并且任何人都可以打破它),我们可以通过<<向数组添加元素,值得提醒的是,Ruby的数组和我们通常使用的数组不同,它的长度是可变的。
此外,Ruby还提供了OpenStruct,和Struct不同的是,OpenStruct创建出来的是对象而不是类,说到这里,你可以感到奇怪,如果是对象的话,自定义的属性要如何设置呢?这正是OpenStruct的独特之处,自定义的属性会在第一次使用时自动创建!下面,我在irb里示范用OpenStruct创建Book对象:

图 6
首先,我通过require引用OpenStruct的库,接着,我通过new方法创建了一个OpenStruct对象,然后,我通过反射查看这个对象提供的方法,随后,每设置一个属性的值,我就通过反射来查看这个对象的方法,正如你所看到的,自定义的属性确是在第一次使用时自动创建。另外,你有没有发觉,这次的irb和前面看到的不一样?这是因为我在启动它的时候使用了--simple-prompt命令行选项,这可以简化irb提示符的显示,你可以对比图1感受一下它们之间的区别。
类成员
现在,我需要一个书架帮我管理我的书,这个书架提供存放和查找功能,我们将会通过这个书架了解Ruby的类成员的写法。
下面,我们创建一个BookShelf类,并且在它里面创建一个静态的哈希表,用来存放Book对象:

代码 13
在Ruby里,静态字段以@@开头,{}则用来创建一个空的哈希表。接下来,我们要创建两个静态方法:place和find,分别用于存放和查找Book对象:

代码 14
在Ruby里,创建静态方法是非常简单的,代码12示范了两种风格,第一种是把类名置于方法名字之前,中间用.分开;第二种是把self置于方法名字之前,中间用.分开,这两种风格是等效的,区别只在于个人喜好。哈希表可以通过[]来访问里面的数据,当你看到代码12时,你可能会问,为什么place方法和find方法都没有事先检查key是否存在,你可以这样做,但没有必要,因为在调用place方法时,如果key已经存在,则用新的value代替现有的,如果key并不存在,则往里面添加这对key/value,而在调用find方法时,如果key已经存在,则返回对应的value,如果key并不存在,则返回nil,这些是哈希表的[]预设的行为。
除了上述两种风格,Ruby还支持另外两种看起来有点怪异的风格,第一种是创建一个实例方法,然后把它包在class << self和end之间:

代码 15
这种做法咋看比较怪异,但有时候确是必须的,比如说,我现在想用attr_accessor来为path创建静态字段和读/写访问器,显然,之前介绍的两种做法都无能为力,因为最终代码是由attr_accessor方法代为生成的,这时就轮到class << self出场了,我只需把attr_accessor :path放在class << self和end之间就行了。
第四种风格可以看作第三种的变形,它们之间的区别在于第三种风格的代码是放在类里面的,而第四种风格的代码则放在类外面,于是,class << self需要改成class << BookShelf,以便告知这份代码的所属:

代码 16
现在,我们来看看BookShelf的使用情况(变量b的初始化请参见代码10):

代码 17
运行结果如下:

图 7
Ruby支持字符串插值(String Interpolation),当我们用#{}包围某个表达式时,这个表达式的运算结果将被插入表达式所在的位置,从运行结果可以清楚地看到这点。另外,我在puts后面添加了unless XXX,这将导致puts语句在unless后面的条件满足时不执行,当然,如果你不嫌冗长,你也可以把它改成:

代码 18
效果都是一样的。
现在,回到前面的问题,new方法是从哪里来的呢,还有,我们定义的initialize方法好像从来没有调用过,又是谁给那些私有字段初始化的呢?下面,我们将会使用irb来探个究竟:

图 8
首先,我创建了一个Book类,里面只有initialize方法,当我用new方法创建一个Book对象时,我们从命令行的输出结果可以看到initialize方法已被调用,接着,我对Book类进行扩展,添加一个new方法,从前面的调用来看,它是一个静态方法,因此,你可以选择上述四种风格的任意一种来实现,然后,我再用new方法创建一个Book对象,这次,我们从命令行的输出结果可以看到new方法和initialize方法都被调用,而且new方法的调用在initialize方法之前。细心的你可能已经发现,new方法的实现里包含了一个super关键字,这是什么意思呢?在回答这个问题之前,我们做另一个实验,就是把这个关键字去掉,看看会有什么不同:

图 9
当我们把super关键字去掉之后,从命令行的输出结果不难发现,initialize方法"失效"了,并且new方法不再创建对象!综上所述,new方法是initialize方法得以调用的关键,事实上,new方法是Book类从Class类那里继承过来的一个方法,它的职责就是创建(未初始化的)对象,并调用initialize方法来初始化这个对象(initialize方法是私有的,你无法直接调用它),我们刚才为Book类重写了这个方法,为了使它依然生效,我们需要调用"原本的"实现,因此需要加上super关键字,就目前而言,你只要记住super关键字的作用是调用当前方法的继承版本就行了。
新的旅程
上一节末尾,我们提到了继承,这是面向对象编程的核心概念之一,那么,Ruby又是如何体现继承以及面向对象编程的其他特征呢,我们将会在以后的文章里逐一探讨。
P.S. 本文的代码,若无特别说明,均可在Ruby、IronRuby和JRuby上运行,笔者所使用的版本分别是1.9.1、0.9.0和1.3.1。
相关推荐
1. **Ruby**: Ruby是一种面向对象的、动态类型的编程语言,以其简洁和可读性强的语法著称,非常适合快速开发和脚本编写。 2. **Selenium**: Selenium是一组用于Web应用程序测试的工具,支持多种编程语言,包括Java...
Ruby具有“彻底面向对象”、“丰富的程序库”、“亲和力高的直观语法形式”等多项特征,但这些特征并不是Ruby的目的,只能说是为了让程序设计更有趣而开发的手段罢了。 希望《Ruby Programming:向Ruby之父学程序...
在Ruby中,类是用来定义具有相同属性和行为的对象的模板。类的定义使用`class`关键字开始,以`end`关键字结束。 ```ruby class ClassName def method_name # 方法体 end end ``` - `ClassName`是类名,按照Ruby...
总结来说,Ruby的方法和类是其面向对象特性的核心。方法提供了代码的重用,而类则定义了对象的结构和行为。通过继承和模块,Ruby提供了灵活的代码组织和扩展机制,使得开发者能够构建出复杂的、可维护的软件系统。
总的来说,Ruby的面向对象枚举通过类的形式实现了枚举的创建、遍历和扩展,这使得Ruby的枚举功能既强大又灵活。在处理有限集合的常量时,面向对象的枚举是开发者的一个得力工具。通过这种方式,我们可以更好地组织...
101:学习 Ruby 编程语言 在这里,我描述了我学习 Ruby 的历程,Ruby 是一种非常酷且快速的面向对象的解释型编程语言。 有一些编程概念的基本知识是很好的开始。 本文档会定期更新,我也会提供大量示例代码和程序。 ...
库序列化 Ruby 对象,在许多方面进行了优化: * 快速且小巧:使用 MessagePack(二进制紧凑型存储)并且不会将同一对象序列化两次 * 独立于 Ruby 版本:跨不同版本转储和加载数据 * 保留共享对象:如果一个对象被...
《Ruby面向对象设计实践》不仅讲解了面向对象编程的基本概念和技术,还深入探讨了如何应用设计原则和设计模式来编写更高质量的代码。 ##### 1. SOLID原则 - **单一职责原则**(Single Responsibility Principle, ...
- **动态类和方法定义**:使用`class_eval`方法动态定义类和方法。 #### 3.3 并发编程 - **线程(Thread)**:Ruby支持多线程编程,可以轻松实现并发任务处理。 - **进程(Process)**:除了线程之外,还可以使用进程来...
在Ruby中,类是一个数据类型的蓝图,定义了对象的结构和功能。 2. **对象(Object)**:对象是由类创建的具体实体。每个对象都具有类定义的属性和行为,并且每个对象都是独立存在的,拥有自己独特的状态。 #### 三...
Ruby的MongoMapper库就是为了让开发者能够更自然地在Ruby对象和MongoDB文档之间进行映射,从而简化数据操作。 MongoMapper的设计理念深受ActiveRecord的影响,它是Ruby on Rails框架中的核心组件,但MongoMapper...
类和对象:理解类和对象的概念,学习如何定义类、创建对象以及封装数据和方法。 继承和多态:掌握继承的概念,以及如何通过多态实现接口和抽象类。 模块和混合: 学习如何使用模块来组织代码,以及如何使用混合...
6. 对象和类:深入理解Ruby的面向对象编程,包括类的定义、继承、模块的使用,以及构造函数和析构函数。 7. 异常处理:了解如何处理程序中的错误,使用rescue来捕获和处理异常。 8. 文件I/O:学习如何读写文件,...
这个库的核心功能是将Nmap的XML输出转换为Ruby对象,这样开发者可以轻松地遍历、分析和操作这些对象。例如,你可以获取到扫描中发现的每个主机的信息,包括它们的IP地址、端口状态、开放的服务以及操作系统猜测。...
内容概要:本文是一份详尽的Ruby语言教程,从Ruby的基本概述入手,介绍了其主要特点、环境搭建、基础语法、面向对象编程概念、高级特性和Web开发框架Rails的应用。主要内容包括:Ruby的特点、安装方法、变量和数据...
- **对象**:所有事物在 Ruby 中都被视为对象。 - **类**:类是创建对象的模板。可以定义属性和方法来描述对象的行为和状态。 - **继承**:子类可以从父类继承属性和方法,这样可以重用代码并提高模块性。 - **封装*...
在Ruby编程语言中,面向对象编程(OOP)是一项核心特性,而理解类和对象的概念则是掌握Ruby编程的基础。本文将深入探讨Ruby中的类对象概念,帮助读者更好地理解和应用这一基础知识。 #### 二、类与对象的基本概念 ...
此外,httparty还有一个强大的特性是它能直接转换JSON或XML响应到Ruby对象。这得益于它内置的解析器,如JSON和Nokogiri,它们允许你方便地解析和操作响应数据。 在实际项目中,httparty通常与其他库(如ActiveModel...
迈克·克拉克,作者兼顾问,认为对于那些深陷Java世界的开发者来说,《Programming Ruby》是一本不可多得的优秀指南,它能揭示Ruby所带来的惊喜和乐趣,让读者在接触Ruby之后难以回到其他语言的怀抱。 #### 詹姆斯...