SlideShare une entreprise Scribd logo
1  sur  14
Télécharger pour lire hors ligne
介绍

开始 Ruby 之旅
此时你正在读的是一本关于 Ruby 的书,我想我完全可以假设你已经不需要我来强调 Ruby
的优点。相反,我将在开始的时候给大家一个警告:许多的人被 Ruby 的简单语法而吸引。
他们错了。在刚接触 Ruby 的时候,它的语法看起来是比较简单,但是,当你越来越多地了
解 Ruby 之后,你就会发现,与之相反的是 Ruby 的语法极其复杂。这个问题的本质是
Ruby 给那些不够谨慎的程序员留了很多陷阱(pitfalls)等着他们来钻。

在这本书中,我的目的就是让您安全地避开这些陷阱,然后带领您穿越 Ruby 语法和类库的
洪流。在这个过程中,我既将探索 Ruby 的康庄大道也偶尔拐到一些崎岖不平的小道中去。
在旅程结束之后,您就将摆脱一切意外的危险,安全而有效地使用 Ruby 了。


如何阅读本书
这本书分成了很多小章节。每一章节介绍一个主题,并分多个小课题进行讲解。每一个编程
的课题都是配对了相应的一个或者多个独立的可以真正运行的小 Ruby 程序。

如果你想遵循一个结构良好的课程的话,请依序阅读每一章。如果你更倾向于从实践开始入
门的学习,你可以先运行这些程序,在你需要一些相关解释的时候再来参考章节的内容。如
果你已经有了一些 Ruby 编程的经验,那你就可以随意地选取你认为有用的话题来阅读。这
本书中的程序均是完全独立的,你完全不必担心不按顺序来阅读会成为“傻子”。


深入探讨
每一章除了主要部分还包含一节名为“深入探讨”。在这一节中,我们将会深入探讨 Ruby
特殊的性质(包括之前我提到的“崎岖小路”)。一般情况下,你可以略过“深入探讨”这一
节,同样能学到使用 Ruby 所需的所有知识。另一方面,在“深入探讨”中,我们经常会深
入了解到 Ruby 底层的运作方式,如果你略过了这些,你就会遗漏掉一些非产有趣的事情。


理解这些文本
在这本 Ruby 书中,所有的 Ruby 代码都是这样书写的:

  def saysomething
   puts(“hello”)
  end
如果有相应的示范程序与之对应,该程序名将在该页右侧的一个盒子中显示,如图:
helloname.rb

注释说明(一般用于提供一些提示或者为文本中的要点提供一个更为深入的解释)将会出现这
样的盒子中:
    这是一个注释。你可以略过它,
    但是如果你真的这么做,你会遗漏掉很多有趣的事情

Ruby 和 Rails

什么是 Ruby?
Ruby 是一个跨平台的编程语言,它和其他的脚本语言如 Perl、Python 有着很多的共同点。
乍一看,它的自然英语式的语法与一些类 Pascal 语言的风格很像。它是完全面向对象的,
并且和“纯”面向对象语言的老祖宗—Smalltalk 有着很多共同之处。据说影响 Ruby 开发
的语言有:Perl,Smalltalk,Eiffel,Ada 和 Lisp。Ruby 是由 Yukihiro Matsumoto(通常称
为''Matz)创造的,在 1995 年首次发布。


什么是 Rails?
目前围绕 Ruby 的令人振奋的消息大多都是关于一个名为 Rails 的 web 开发框架—-即众所
周知的”Ruby On Rails”。Rails 是一个非常出色的框架,但是它并不是 Ruby 的终极目的,
也不是 Ruby 的终结。确实,如果你在没有精通 Ruby 之前就跳到 Rails 开发行列之中,在
创建完一个应用之后,你会发现连你自己都没理解它是什么意思(这也确实是很多 Ruby On
Rails 新手的共同点)。理解 Ruby 是理解 Rails 的必须前提。


下载 Ruby
你可以从 http://www.ruby-lang.org 上下载最新版本的 Ruby。确保你下载的是二进制发
布包而不仅仅是源码。在 PC 机上你可以使用 Ruby Installer for Windows 来安装 Ruby:

            http://rubyinstaller.rubyforge.org/wiki/wiki.pl

另外,如果你正在使用 Ruby In Steel IDE,你可以安装 Ruby,Rails,Ruby In Steel 和所有其
他的在使用 Ruby In Steel 中需要的工具,通过”All-in-one installer”来安装,该安装程
序可以在下面的页面下载:

            http://www.sapphiresteel.com/


获取示范程序的源码
本书每一章中的程序都可以从 http://www.sapphiresteel.com/The-Book-Of-Ruby 处以
Zip 存档格式下载。当你解压这些程序,你会发现他们分章节地存放在一组目录中。使用
Ruby In Steel(一个由本书作者所在公司开发的 Visual Studio 的集成 IDE)的程序员的好处
是,你可以以 Visual Studio 解决方案加载到 Visual Studio 2008 的 Ruby In Steel 环境中,
每一章的程序将以树型在工程管理控件中显示。Visual Studio 2005 的 Ruby In Steel 用户
可以导入或者转化这些工程(通过文件/新建菜单)。


运行 Ruby 程序
在存放 Ruby 源码的目录下打开一个命令行窗口非常有用。只要你的机器上正确地配置了
Ruby 解释器,你便可以通过输入 ruby <程序名>来运行你的程序了,如下:

  ruby 1helloworld.rb

如果你正在使用 Ruby In Steel,你可以通过 Ctrl+F5 在交互控制台运行这些程序,或者 F5
在调试器中运行。


Ruby 库参考文档
本书涵盖了标准 Ruby 库中大部分类以及方法,但是并不意味着所有。偶尔,你需要查看
Ruby 使用到的所有类。幸运的是 Ruby 类库就包含了嵌入的文档,并且已经提取并编译成
多种格式易于浏览的参考。比如说,参考多窗格显示的在线文档:

     http://www.ruby-doc.org/core

另外,也可以从这里按字母序列浏览到该库

     http://www.ruby-doc.org/stdlib

上面的页面中包含了下载离线文档的说明。同样有一个页面关于多格式多语言版本类库参考
文档的下载:

     http://www.ruby-doc.org/downloads

OK,引文到此,下面让我们开始吧。是时候开始第一章了。
第一章

字符串 数字 类 对象
了解到 Ruby 的第一件事便是 Ruby 的易用性。为了证明这一点,让我们来看看最传统也最
经典的”Hello World”程序。如下:
                                       1helloworld.rb

  puts 'hello world'

这就是它的全部。一个方法,puts,和一个字符串,”hello world”。没有头文件和类定
义,没有导入块和“main”方法。确实够简单。加载代码,试着运行。


获取和打印输入
已经打印出一个字符串到输出了(在这里,还只是在命令行窗口),下一步就是如何获取一个
字符串。和你猜的一样,这个 Ruby 方法就是 gets。2Helloname.rb 程序提示用户输入他
/她的名字,我们假设它是“Fred”,然后显示一行问候语:”Hello Fred”。代码如下:
                                       2helloname.rb

  print('Enter your name:')

  name = gets()

  puts(“Hello #{name}”)

同样依然很简单,只有很少的重要的细节需要解释。首先,注意我用了 print 而不是 puts
来打印提示语。这是因为 puts 在字符后面会自动添加一个换行符而 print 不会;在这里我
想光标能继续停留在和提示语同一行。

在下一行,我使用 gets()去读取一个字符串,当用户按下回车键的时候就获取了用户的输入。
这个字符串被赋值给了变量 name。我并没有预先声明该变量,也没有声明其变量类型。在
Ruby 中你可以在你需要使用的时候才创建变量,并且由 Ruby 自动检测判断其类型。在这
里我赋了一个字符串给 name 变量,所以 Ruby 知道 name 的类型肯定是一个字符串了。

    注意: Ruby 是大小写敏感的。一个变量名为 myvar 和 myVar 是不一样的
                               。
   一个和 name 一样的变量一定要是小写字母开头的 ( 如果它以大写字母开头
                               ,
   Ruby 会把它当成一个常量来处理 ---- 我将会在后面的章节详细讲解常量的
                              概念 )
另外,gets()后面的括号和 print 和 puts 后用于括字符串的括号都是可选的;你删除这些
括号之后代码的运行结果是一样的。然而,括号有助于消除歧义,在一些情况下,如果你省
略了括号,Ruby 解释器会警告你让你加上。


字符串和嵌入式求值
在我们的示范代码中最后一行格外有意思:

   puts(“Hello #{name}”)

在这里 name 嵌入到了一个字符串中。这是通过将变量名写在两个化括号之间并在前面加
上一个哈希(英镑)符号#{}。这种嵌入式的求值只能在由双引号引住的(界定的)字符串中才能
正常使用。如果你尝试用在单引号界定的一个字符串中这样使用,变量将不会求值,字符
串'Hello #{name}'将会按照其输入的方式打印出来(译者注:结果即 Hello #{name},而不
是 Hello Fred)。

不仅仅是变量可以嵌入到双引号界定字符串中。你也可以将非打印字符如”n”和”t”嵌
入进去。你甚至可以在双引号字符串中嵌入一小段代码和数学表达式。假设你已经有了一个
名为 showname 的方法,方法返回字符串”Fred”。

下面的字符,在求值的过程中将会调用 showname 方法,结果是打印字符串”Hello
Fred”:

   puts “Hello #{showname}”

看看你能不能算出下面的代码将打印出什么:

   puts(“nt#{(1+2)*3}nGoodbye”)       3string_eval.rb

好的,现在运行 3string_eval.rb 程序,看看你是不是对的。


数字
数字的使用就和字符串一样简单。假设你要通过一些款项的税前价格或者小计来计算其销售
价格或者总计金额,你需要将小计与相应的税率相乘,然后再加上小计值。假设小计为
100,税率为 17.5%,以下 Ruby 程序将进行计算并打印其结果:

                                          4calctax.rb

   subtotal = 100.00

   taxrate = 0.175

   tax = subtotal * taxrate
puts “Tax on $#{subtotal} is $#{tax},so grand total is $#{subtotal+tax}”

显然,如果这个程序能计算不同种类的小计而不只是一遍遍地计算同样的值会更加有用。这
里有一个简单的版本,会提示用户输入小计值:

   taxrate = 0.175

   print "Enter price (ex tax): "

   s = gets

   subtotal = s.to_f

   tax = subtotal * taxrate

   puts "Tax on $#{subtotal} is $#{tax},so grand total is $#{subtotal+tax}"

这里的 s.to_f 是 String 类的一个方法。它将 string 转化为浮点数。比如,字符串
”145.45”将会转化为浮点数 145.45。如果该字符串不能被转化,返回 0.0。所以,
”Hello world”.to_f 将返回 0.0。



      注释 ...
      随这本书发布的很多源码都有很多的注释文档,这些注释文档是被 Ruby 解
      释器忽略的。
      注释一般都是在英镑 ( 哈希 ) 符号#之后。在该字符之后的同一行文本均为
      注释:
      #this is a comment
      Puts(“hello”) #this is also a comment
      如果你想书写多行注释,你可以在多行文本的开头加上 =begin 在结尾加上
      =end
      (=begin 和= end 必须左对齐 ):
      =begin
          This is a
          Multiline
          Comment
      =end
条件                                                                            判断语
     句:IF … THEN
上面的那个简单的税收计算器存在一个问题就是它接受负的小计值并计算出负的税收—这种
状况可是政府部门不愿意看到的!因此需要检查负值,如果出现负值将其设置为 0。这是我
                                                              5taxcalculator.rb
新版本的代码:



  taxrate = 0.175

  print "Enter price (ex tax): "

  s = gets

  subtotal = s.to_f

  if (subtotal < 0.0) then

   subtotal = 0.0

  end

  tax = subtotal * taxrate

  puts "Tax on $#{subtotal} is $#{tax},so grand total is   $#{subtotal+tax}"

Ruby 中的条件判断和其他语法中相似。需要注意的是,括号仍然是可选的,而且关键词
then 可是可选的。然而,如果你打算像下面那般书写代码,在条件判断后面不换行,那么
then 是必须的:

  if(subtotal < 0.0) then subtotal = 0.0 end

这样将所有的代码写在一行不能给代码增加任何可读性,我更倾向于防止这样写代码。由于
我长时间的 Pascal 编码经历,我会本能地在 if 条件后添加一个 then ,而这个真的是没有必
要,你可以认为这是我个人的一点点小偏执。关键词 end 用于结束 if 语句块,它是必须的。
忘记了添加 end 你的代码将无法运行。


局部变量和全局变量
在上一个例子中,我给 subutotal,tax 和 taxrate 等变量进行了赋值。这些变量均以小写字
母开头,它们都是局部变量。这就意味着它们只是存在于程序的某一指定部分,也就是说,
它们受限于一个定义明确的范围。下面是一个例子:

                                                                variables.rb
  localvar = “hello”

  $globalvar = “goodbye”

  def amethod

      localvar = 10
puts( localvar )

      puts( $globalvar )

  end

  def anotherMethod

      localvar = 500

      $globalvar = “bonjour”

      puts( localvar)

      puts( $globalvar )

  end

在例子中有三个局部变量名为 localvar。一个在程序的“main 范围”赋值为”hello
world”,另外两个都是在单独的方法中赋值:因为每个局部变量所处的范围不同,对任意
一个变量赋值都不会影响到其他范围中同名变量的值。你可以通过依序调用这些方法来验证
一下:

  amethod               #=>localvar = 10

  anotherMethod         #=>localvar = 500

  amethod               #=>localvar = 10

  puts( localvar )      #=>localvar = “hello”

而另一方面,全局变量—以美元符$开头的—拥有全局范围。当我们在一个方法中对全局变
量进行赋值,将会影响到其在程序别处的值:

  amethod               #=>$globalvar = “goodbye”

  anotherMethod            #=>$globalvar = “bonjour”

  amethod               #=>$globalvar = “bonjour”

  puts($globalvar) #=>$globalvar = “bonjour”


类和对象
我们本应继续深入到 Ruby 的语法中,去了解它的类型、循环、模块等等(不用担心,我们
之后还会回到这些概念的),然而我想我们应该尽可能快地了解一下如何创建类和对象。


  类、对象和模块
  类是对象的蓝图。它定义了一个对象包含的数据和它的行为方式。许多不同的对
  象可以通过一个类创建。所以你可能只有一个 Cat 类,但是会有三个 cat 对
  象 :tiddles,cuddles 和 flossy 。方法 ( 函数 ) 或者子程序都在类内部定义。
宣称 Ruby 是面向对象的看起来不见得就有多了不起啊。这段时间不是所有的语言都宣称是
面向对象的吗?对,一针见血。现代大多数的“面向对象”编程语言(Java,C++,Object
Pascal 等等)都或多或少的表现出了一定程度上的 OOP(Object Oriented Programming)的
特性。然而 Ruby 是完全面向对象的。事实上,除非你曾经使用过 Smalltalk 或者 Eiffel(比
Ruby 更面向对象)进行过开发,它差不多是你用过的编程语言中最为面向对象的。每一种数
据—从简单的数字和字符串到复杂的文件和模块--在 Ruby 中均为对象。并且对类的操作几
乎都是通过方法来完成的。即使像”+”和“-”这样的算术运算符都是方法。思考下面的
代码:

  x=1+2

这里的”+”是一个整型数对象 1 的方法。数值 2 传递给该方法,返回结果为 3,将结果赋
值给对象 x。然而,“=”运算符却是“类的操作均为方法”原则外极少数的异类。这个赋
值运算符是一个特殊内建机制(这不是正式的术语,只是我自己加上的)不是任何类的方法。

现在让我们来看看如何创建一个自己的对象。和大部分 OOP 语言一样,Ruby 的对象也是
通过类来定义的。类就像即将创建的对象的蓝图一般。下面的例子定义了一个 Dog 类:

  class Dog

      def set_name(aName)

         @myname = aName

      end

  end

类的定义由关键字 class(均小写)开始,后跟类名,类名必须以大写字母开头。这个类有一个
方法为 set_name。它有一个传入参数,aName。方法内部将该参数 aName 的值赋给了一
个名为@myname 的变量。


实例变量
由@字符开头的变量是”实例变量”--这意味着他们隶属于单个对象--或者说是类的实例。
实例变量也不需要预声明。我可以通过 new 方法创建 Dog 类的一个实例(即 Dog 对象)。在
这里,我创建两个 Dog 类的实例(注意:类名以大写字母开头而对象名以小写字母开头):

  mydog = Dog.new
yourdog = Dog.new

现在这两条狗还没有名字呢,所以下一步要做的就是给这两条狗起名字,调用 set_name 方
法给它们起名:

     mydog.set_name('Fido')

     yourdog.set_name(’Bonzo')

已经给每条狗都起了一个名字,然后我需要什么方法来查看他们的名字。我该怎么办呢?我
不能到一个对象的内部去获取@name 变量,因为每一个对象的内部细节只有对象自己知道。
这是“纯”面向对象的基本原则:每个对象内部的数据是私有的。进出对象的方法是明确定
义的。只有对象自身可以更改其内部的状态。外部世界对此无能为力。这称为“数据隐藏”,
是“封装”原则的一部分。




       封装:
       在 ruby 中,封装并不像它最初那般严格要求。有一些相当恶心的陷阱,让你能够在对
       象内部为所欲为。为清楚起见 ( 也为了确保你和我不会做噩梦 ) ,我现在先掠过 Ruby
       的这些特性。

因为我们需要每条狗都知道它们自己的名字,所以让我们给 Dog 类提供一个 get_name 方
法:

     def get_name

        return @myname

     end

return 关键词在这里不是必须的。当省略 return 的时候,Ruby 方法会返回方法体内最后一
个表达式的值。

为了清楚起见(也为了防止比这个方法复杂的方法返回意外值),我习惯于显式地的返回我计
划使用的值。

最后,让我们给这些狗们添加一些行为,比如说让它叫。这是完整类的定义:

     class Dog

        def set_name( aName )

           @myname = aName

        end
def get_name

        return @myname

     end

     def talk

         return 'woof!'

     end

  end

现在,我们可以创建一个 Dog 的实例了,给他命名,并显示它的名字,让它叫:

  mydog = Dog.new

  mydog.set_name('Fido')

  puts(mydog.get_name)

  puts(mydog.talk)

                                          6dogs.rb


我在 6dogs.rb 中写了一个上面代码的扩展版。在程序中还包含了一个跟 Dog 类似的 Cat 类,
只是在它们的 talk 方法上存在一定的差异,只是返回”miaow”来代替”woof”。


   喔!这个程序好像有错误。
   名为 someotherdog 的对象的 @name 变量从未被赋值。幸运的是, Ruby 并
   不会在我们试图打印这条狗的名字时候发飙。相反的是,它只是打印出 'nil' 。我
   们将快速地看看保证这种错误不再发生的简单方法。




消息,方法和多态
这个例子是基于一个经典的 Smalltalk 的 demo 程序,它描述了相同的“消息”(如 talk 方
法)可以传递给不同的对象(如 cats 和 dogs),针对同一个消息,对象调用自身特定的方法
(这里是 talk 方法)反应却不相同。不同的类拥有同名的方法在面向对象中称为”多态”--一
个一旦记住就很难忘掉的词。

当你在运行一个类似 6dongs.rb 的程序时,代码是依序执行的。这些类的代码只在程序下
方中创建对象的代码之后才执行。你将看到我经常是把类的定义和在程序运行时独立的代码
段混搭着写。这也许不是你写一些重要应用程序时的选择,但是在你想测试一些东西的时候
就是非常便利的了。

   独立代码段 ...?
   如果 Ruby 是一个真正的面向对象的语言,你可能会觉得可以创建独立的悬挂方法是奇怪的。
   但事实上,当我们在运行一个程序的时候, Ruby 创建了一个 main 对象以及其他所有在
   同一个代码单元中的代码,不论是什么形式的代码,根本就是不什么独立悬挂的代码,实际
   上都是运行在 main 对象内。你可以很容易的验证这一点。创建一个新的源文件,添加下面
   的代码,然后运行,查看其输出:
        put self
        puts self.class

这个程序的明显的缺点就是那两个类,Cat 和 Dog 太重复了。如果只有一个 Animal 类,它
有 get_name 和 set_name 方法的话,并由此派生两个类 Cat 和 Dog,那就有意义多了。
在 Cat 和 Dog 类中分别包含各自的方法 woofing 或者 miaowing,针对不同的动物设置不
同的行为。我们将在下一章探讨如何实现。


构造器—创建和初始化
现在,让我们来看看另外一个用户自定义类的例子。加载 7treasure.rb。这是一个正在制作
中的冒险游戏。它包含了两个类,Thing 和 Treasure。Thing 类和上一个程序中的
Cat,Dog 类很相似,区别就是它没有 woof 或者 miaow 的行为。

Treasure 类没有 get_name 和 set_name 方法。它包含一个名为 initialize 的方法,该方法
有两个参数,分别赋值给@name 和@description 变量。

                                               7treasure.rb

  def initialize(aName, aDescription)

     @name                = aName

     @description = aDescription

  end

当一个类包含了一个名为 initialize 方法的时候,当对象是通过 new 方法创建的时候将自动
调用该方法。使用 initialize 方法来设置对象实例变量的值是一个好主意。

这比使用 set_name 这样的方法来设置每一个实例变量值很显然多了两个好处。首先,一个
拥有众多实例变量的类,可以通过这一个 initialize 方法来初始化所有的变量的值,而不需
要通过众多的独立”set”方法来给这些变量赋值;其次,如果所有的变量在对象创建的时
候就自动初始化好了,你将永远不会因为一个“空”值(就像在我们在上一个程序中试图打
印 someotherdog 的名字时返回 nil 值)而出错。

最后,我创建了一个方法 to_s 用来返回一个 Treasure 对象的字符串描述。这个方法名 to_s
不是随便起的。与之同名的方法在 Ruby 标准对象的整个层次结构中都在使用。事实上,
to_s 方法是为 Ruby 中所有类的终极祖先 Object 类定义的。通过重定义 to_s 方法,我添加
了一些比默认方法更适合于 Treasure 类的行为。也就是说,我重载了它的 to_s 方法。

new 方法创建对象,所以可以将其视为对象的构造器。然而,一般不能实现你自己的 new
方法(这是可能的,但是并不建议这么做)。如果你想执行任何”setup”行为—比如说赋值
给对象的实例变量,你可以在 initialize 方法中做。Ruby 将会在对象创建之后立刻调用
initialize 方法。

    垃圾回收:
    在很多语言中,如 C++ 和 Delphi(Win32) ,它是需要程序员负责销毁其创建后不再使
    用的对象。也就是说,对象同时需要析构器和构造器。在 Ruby 中,你不需要这么做,
    因为 Ruby 有一个内置的“垃圾回收器”将在对象不在被你的程序引用的时候自动销毁
    对象并回收它们使用到的内存。




检测对象
另外,大家可能也注意到了我查看了 Treasure 对象 t1 的内部结构,通过 inspect 方法:

   t1.inspect

所有的 Ruby 对象均定义了 inspect 方法。它返回一个关对对象的易读的字符串。在这个例
子中,它打印的信息如下:

   #<Treasure:0x28962f8 @description="an Elvish weapon forged of     gold",
@name="Sword">

它以类名 Treasure 开头,随后是一个数字,可能跟上面的不一样--这是 Ruby 内部针对
这个对象的识别码;然后是对象的变量名和变量的值。

Ruby 通过提供了一个快捷的方法来检测对象并将起详细信息打印出来,这个方法就是 p:

                                                              p.rb

   p(anobject)

运行 8to_s.rb,查看 to_s 怎么可以由这么多的对象调用,测试一个 Treasure 对象怎么在不
重载 to_s 方法的情况下,将对象转化为一个字符串。
                                                            8to_s.rb
puts(Class.to_s)   #=> Class

  puts(Object.to_s) #=> Object

  puts(String.to_s) #=> String

  puts(100.to_s)     #=> 100

  puts(Treasure.to_s) #=> Treasure

你将会看到,像 Class,Object,String 和 Treasure 这些类,在调用 to_s 方法时都会返回它
们的名字。像 Treasure 这样的对象 t,还将会返回它的标识码--和 inspect 方法返回的识
别码是相同的:

  t = Treasure.new( "Sword", "A lovely Elvish weapon" )

  puts(t.to_s)

  #=> #<Treasure:0x3308100>

  puts(t.inspect)

   #=> #<Treasure:0x3308100

   @name="Sword", @description="A lovely Elvish weapon">

虽然 7treasure.rb 给一个包含众多不同对象的游戏打下了基础,但是它的代码重复性依然太
高(冗余性过高)。毕竟,为何 Thing 类和 Treasure 类都包含一个名字呢?将 Treasure 视为
Thing 的一种岂不更有意义?在一个完整的游戏中,其他的对象,例如 Rooms 和 Weapons
可以是其他类型的 Thing。是时候开始进入到类继承的内容了,下章我们将开始...

Contenu connexe

Tendances

Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closureswang hongjiang
 
Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Harvey Zhang
 
JavaScript 快速跳坑指南
JavaScript 快速跳坑指南JavaScript 快速跳坑指南
JavaScript 快速跳坑指南MuJingTsai
 
深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)wang hongjiang
 
Java Script 引擎技术
Java Script 引擎技术Java Script 引擎技术
Java Script 引擎技术bigqiang zou
 
Introduction to Parse JavaScript SDK
Introduction to Parse JavaScript SDKIntroduction to Parse JavaScript SDK
Introduction to Parse JavaScript SDK維佋 唐
 
看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)
看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)
看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)永立 連
 
iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门Lucien Li
 
Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则YUCHENG HU
 
PHP Coding Standard and 50+ Programming Skills
PHP Coding Standard and 50+ Programming SkillsPHP Coding Standard and 50+ Programming Skills
PHP Coding Standard and 50+ Programming SkillsHo Kim
 
C++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesC++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesPeien Luo
 
Java易犯错误
Java易犯错误Java易犯错误
Java易犯错误yiditushe
 
JavaScript 教程
JavaScript 教程JavaScript 教程
JavaScript 教程Bobby Zhou
 
Ecmascript基础
Ecmascript基础Ecmascript基础
Ecmascript基础enmaai
 
看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)
看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)
看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)永立 連
 

Tendances (18)

Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
 
Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版
 
JavaScript 快速跳坑指南
JavaScript 快速跳坑指南JavaScript 快速跳坑指南
JavaScript 快速跳坑指南
 
深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)
 
Java Script 引擎技术
Java Script 引擎技术Java Script 引擎技术
Java Script 引擎技术
 
Introduction to Parse JavaScript SDK
Introduction to Parse JavaScript SDKIntroduction to Parse JavaScript SDK
Introduction to Parse JavaScript SDK
 
看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)
看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)
看似比較簡單堆推坑教學 C語言崩潰到崩潰(一)
 
iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门
 
Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则Moodle 项目帮助手册:程序编写准则
Moodle 项目帮助手册:程序编写准则
 
ios分享
ios分享ios分享
ios分享
 
第五章
第五章第五章
第五章
 
PHP Coding Standard and 50+ Programming Skills
PHP Coding Standard and 50+ Programming SkillsPHP Coding Standard and 50+ Programming Skills
PHP Coding Standard and 50+ Programming Skills
 
C++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesC++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New Features
 
Java易犯错误
Java易犯错误Java易犯错误
Java易犯错误
 
JavaScript 教程
JavaScript 教程JavaScript 教程
JavaScript 教程
 
泛型总结
泛型总结泛型总结
泛型总结
 
Ecmascript基础
Ecmascript基础Ecmascript基础
Ecmascript基础
 
看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)
看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)
看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(一)
 

Similaire à 介绍&第一章

Learning notes ruby
Learning notes rubyLearning notes ruby
Learning notes rubyRoger Xia
 
第1章 入门
第1章 入门第1章 入门
第1章 入门Runa Jiang
 
Rubyonrails(Chinese)
Rubyonrails(Chinese)Rubyonrails(Chinese)
Rubyonrails(Chinese)heisda
 
實踐大學教案20140329
實踐大學教案20140329實踐大學教案20140329
實踐大學教案20140329Mu-Fan Teng
 
RSpec 讓你愛上寫測試
RSpec 讓你愛上寫測試RSpec 讓你愛上寫測試
RSpec 讓你愛上寫測試Wen-Tien Chang
 
Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)Drake Huang
 
Groovy简介
Groovy简介Groovy简介
Groovy简介profeter
 
Ruby程式語言入門導覽
Ruby程式語言入門導覽Ruby程式語言入門導覽
Ruby程式語言入門導覽Mu-Fan Teng
 
Ksdg 使用 ruby on rails 快速打造你的 web app
Ksdg   使用 ruby on rails 快速打造你的 web appKsdg   使用 ruby on rails 快速打造你的 web app
Ksdg 使用 ruby on rails 快速打造你的 web appEddie Lee
 
数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点thinkinlamp
 
J Ruby和Rails 让Ruby语言融入Java项目
J Ruby和Rails 让Ruby语言融入Java项目J Ruby和Rails 让Ruby语言融入Java项目
J Ruby和Rails 让Ruby语言融入Java项目George Ang
 
Expect中文版教程
Expect中文版教程Expect中文版教程
Expect中文版教程Da Zhao
 
Ruby rails分享
Ruby rails分享Ruby rails分享
Ruby rails分享Cam Song
 
Java7 fork join framework and closures
Java7 fork join framework and closuresJava7 fork join framework and closures
Java7 fork join framework and closureswang hongjiang
 
Ruby的类和对象模型
Ruby的类和对象模型Ruby的类和对象模型
Ruby的类和对象模型yinhm .
 
Xcode开发员入门导引(简体中文版)
Xcode开发员入门导引(简体中文版)Xcode开发员入门导引(简体中文版)
Xcode开发员入门导引(简体中文版)babyyellow li
 
Xcode开发员入门导引
Xcode开发员入门导引Xcode开发员入门导引
Xcode开发员入门导引Sophia Lindsey
 
Scala分享第二期
Scala分享第二期Scala分享第二期
Scala分享第二期Tony Deng
 

Similaire à 介绍&第一章 (20)

Learning notes ruby
Learning notes rubyLearning notes ruby
Learning notes ruby
 
第1章 入门
第1章 入门第1章 入门
第1章 入门
 
Rubyonrails(Chinese)
Rubyonrails(Chinese)Rubyonrails(Chinese)
Rubyonrails(Chinese)
 
第二章
第二章第二章
第二章
 
實踐大學教案20140329
實踐大學教案20140329實踐大學教案20140329
實踐大學教案20140329
 
RSpec 讓你愛上寫測試
RSpec 讓你愛上寫測試RSpec 讓你愛上寫測試
RSpec 讓你愛上寫測試
 
Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)
 
Groovy简介
Groovy简介Groovy简介
Groovy简介
 
Ruby程式語言入門導覽
Ruby程式語言入門導覽Ruby程式語言入門導覽
Ruby程式語言入門導覽
 
Ksdg 使用 ruby on rails 快速打造你的 web app
Ksdg   使用 ruby on rails 快速打造你的 web appKsdg   使用 ruby on rails 快速打造你的 web app
Ksdg 使用 ruby on rails 快速打造你的 web app
 
数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点
 
J Ruby和Rails 让Ruby语言融入Java项目
J Ruby和Rails 让Ruby语言融入Java项目J Ruby和Rails 让Ruby语言融入Java项目
J Ruby和Rails 让Ruby语言融入Java项目
 
Expect中文版教程
Expect中文版教程Expect中文版教程
Expect中文版教程
 
Ruby rails分享
Ruby rails分享Ruby rails分享
Ruby rails分享
 
Java7 fork join framework and closures
Java7 fork join framework and closuresJava7 fork join framework and closures
Java7 fork join framework and closures
 
Ruby的类和对象模型
Ruby的类和对象模型Ruby的类和对象模型
Ruby的类和对象模型
 
Xcode开发员入门导引(简体中文版)
Xcode开发员入门导引(简体中文版)Xcode开发员入门导引(简体中文版)
Xcode开发员入门导引(简体中文版)
 
Xcode开发员入门导引
Xcode开发员入门导引Xcode开发员入门导引
Xcode开发员入门导引
 
Scala分享第二期
Scala分享第二期Scala分享第二期
Scala分享第二期
 
Scala
ScalaScala
Scala
 

介绍&第一章

  • 1. 介绍 开始 Ruby 之旅 此时你正在读的是一本关于 Ruby 的书,我想我完全可以假设你已经不需要我来强调 Ruby 的优点。相反,我将在开始的时候给大家一个警告:许多的人被 Ruby 的简单语法而吸引。 他们错了。在刚接触 Ruby 的时候,它的语法看起来是比较简单,但是,当你越来越多地了 解 Ruby 之后,你就会发现,与之相反的是 Ruby 的语法极其复杂。这个问题的本质是 Ruby 给那些不够谨慎的程序员留了很多陷阱(pitfalls)等着他们来钻。 在这本书中,我的目的就是让您安全地避开这些陷阱,然后带领您穿越 Ruby 语法和类库的 洪流。在这个过程中,我既将探索 Ruby 的康庄大道也偶尔拐到一些崎岖不平的小道中去。 在旅程结束之后,您就将摆脱一切意外的危险,安全而有效地使用 Ruby 了。 如何阅读本书 这本书分成了很多小章节。每一章节介绍一个主题,并分多个小课题进行讲解。每一个编程 的课题都是配对了相应的一个或者多个独立的可以真正运行的小 Ruby 程序。 如果你想遵循一个结构良好的课程的话,请依序阅读每一章。如果你更倾向于从实践开始入 门的学习,你可以先运行这些程序,在你需要一些相关解释的时候再来参考章节的内容。如 果你已经有了一些 Ruby 编程的经验,那你就可以随意地选取你认为有用的话题来阅读。这 本书中的程序均是完全独立的,你完全不必担心不按顺序来阅读会成为“傻子”。 深入探讨 每一章除了主要部分还包含一节名为“深入探讨”。在这一节中,我们将会深入探讨 Ruby 特殊的性质(包括之前我提到的“崎岖小路”)。一般情况下,你可以略过“深入探讨”这一 节,同样能学到使用 Ruby 所需的所有知识。另一方面,在“深入探讨”中,我们经常会深 入了解到 Ruby 底层的运作方式,如果你略过了这些,你就会遗漏掉一些非产有趣的事情。 理解这些文本 在这本 Ruby 书中,所有的 Ruby 代码都是这样书写的: def saysomething puts(“hello”) end 如果有相应的示范程序与之对应,该程序名将在该页右侧的一个盒子中显示,如图:
  • 2. helloname.rb 注释说明(一般用于提供一些提示或者为文本中的要点提供一个更为深入的解释)将会出现这 样的盒子中: 这是一个注释。你可以略过它, 但是如果你真的这么做,你会遗漏掉很多有趣的事情 Ruby 和 Rails 什么是 Ruby? Ruby 是一个跨平台的编程语言,它和其他的脚本语言如 Perl、Python 有着很多的共同点。 乍一看,它的自然英语式的语法与一些类 Pascal 语言的风格很像。它是完全面向对象的, 并且和“纯”面向对象语言的老祖宗—Smalltalk 有着很多共同之处。据说影响 Ruby 开发 的语言有:Perl,Smalltalk,Eiffel,Ada 和 Lisp。Ruby 是由 Yukihiro Matsumoto(通常称 为''Matz)创造的,在 1995 年首次发布。 什么是 Rails? 目前围绕 Ruby 的令人振奋的消息大多都是关于一个名为 Rails 的 web 开发框架—-即众所 周知的”Ruby On Rails”。Rails 是一个非常出色的框架,但是它并不是 Ruby 的终极目的, 也不是 Ruby 的终结。确实,如果你在没有精通 Ruby 之前就跳到 Rails 开发行列之中,在 创建完一个应用之后,你会发现连你自己都没理解它是什么意思(这也确实是很多 Ruby On Rails 新手的共同点)。理解 Ruby 是理解 Rails 的必须前提。 下载 Ruby 你可以从 http://www.ruby-lang.org 上下载最新版本的 Ruby。确保你下载的是二进制发 布包而不仅仅是源码。在 PC 机上你可以使用 Ruby Installer for Windows 来安装 Ruby: http://rubyinstaller.rubyforge.org/wiki/wiki.pl 另外,如果你正在使用 Ruby In Steel IDE,你可以安装 Ruby,Rails,Ruby In Steel 和所有其 他的在使用 Ruby In Steel 中需要的工具,通过”All-in-one installer”来安装,该安装程 序可以在下面的页面下载: http://www.sapphiresteel.com/ 获取示范程序的源码 本书每一章中的程序都可以从 http://www.sapphiresteel.com/The-Book-Of-Ruby 处以
  • 3. Zip 存档格式下载。当你解压这些程序,你会发现他们分章节地存放在一组目录中。使用 Ruby In Steel(一个由本书作者所在公司开发的 Visual Studio 的集成 IDE)的程序员的好处 是,你可以以 Visual Studio 解决方案加载到 Visual Studio 2008 的 Ruby In Steel 环境中, 每一章的程序将以树型在工程管理控件中显示。Visual Studio 2005 的 Ruby In Steel 用户 可以导入或者转化这些工程(通过文件/新建菜单)。 运行 Ruby 程序 在存放 Ruby 源码的目录下打开一个命令行窗口非常有用。只要你的机器上正确地配置了 Ruby 解释器,你便可以通过输入 ruby <程序名>来运行你的程序了,如下: ruby 1helloworld.rb 如果你正在使用 Ruby In Steel,你可以通过 Ctrl+F5 在交互控制台运行这些程序,或者 F5 在调试器中运行。 Ruby 库参考文档 本书涵盖了标准 Ruby 库中大部分类以及方法,但是并不意味着所有。偶尔,你需要查看 Ruby 使用到的所有类。幸运的是 Ruby 类库就包含了嵌入的文档,并且已经提取并编译成 多种格式易于浏览的参考。比如说,参考多窗格显示的在线文档: http://www.ruby-doc.org/core 另外,也可以从这里按字母序列浏览到该库 http://www.ruby-doc.org/stdlib 上面的页面中包含了下载离线文档的说明。同样有一个页面关于多格式多语言版本类库参考 文档的下载: http://www.ruby-doc.org/downloads OK,引文到此,下面让我们开始吧。是时候开始第一章了。
  • 4. 第一章 字符串 数字 类 对象 了解到 Ruby 的第一件事便是 Ruby 的易用性。为了证明这一点,让我们来看看最传统也最 经典的”Hello World”程序。如下: 1helloworld.rb puts 'hello world' 这就是它的全部。一个方法,puts,和一个字符串,”hello world”。没有头文件和类定 义,没有导入块和“main”方法。确实够简单。加载代码,试着运行。 获取和打印输入 已经打印出一个字符串到输出了(在这里,还只是在命令行窗口),下一步就是如何获取一个 字符串。和你猜的一样,这个 Ruby 方法就是 gets。2Helloname.rb 程序提示用户输入他 /她的名字,我们假设它是“Fred”,然后显示一行问候语:”Hello Fred”。代码如下: 2helloname.rb print('Enter your name:') name = gets() puts(“Hello #{name}”) 同样依然很简单,只有很少的重要的细节需要解释。首先,注意我用了 print 而不是 puts 来打印提示语。这是因为 puts 在字符后面会自动添加一个换行符而 print 不会;在这里我 想光标能继续停留在和提示语同一行。 在下一行,我使用 gets()去读取一个字符串,当用户按下回车键的时候就获取了用户的输入。 这个字符串被赋值给了变量 name。我并没有预先声明该变量,也没有声明其变量类型。在 Ruby 中你可以在你需要使用的时候才创建变量,并且由 Ruby 自动检测判断其类型。在这 里我赋了一个字符串给 name 变量,所以 Ruby 知道 name 的类型肯定是一个字符串了。 注意: Ruby 是大小写敏感的。一个变量名为 myvar 和 myVar 是不一样的 。 一个和 name 一样的变量一定要是小写字母开头的 ( 如果它以大写字母开头 , Ruby 会把它当成一个常量来处理 ---- 我将会在后面的章节详细讲解常量的 概念 )
  • 5. 另外,gets()后面的括号和 print 和 puts 后用于括字符串的括号都是可选的;你删除这些 括号之后代码的运行结果是一样的。然而,括号有助于消除歧义,在一些情况下,如果你省 略了括号,Ruby 解释器会警告你让你加上。 字符串和嵌入式求值 在我们的示范代码中最后一行格外有意思: puts(“Hello #{name}”) 在这里 name 嵌入到了一个字符串中。这是通过将变量名写在两个化括号之间并在前面加 上一个哈希(英镑)符号#{}。这种嵌入式的求值只能在由双引号引住的(界定的)字符串中才能 正常使用。如果你尝试用在单引号界定的一个字符串中这样使用,变量将不会求值,字符 串'Hello #{name}'将会按照其输入的方式打印出来(译者注:结果即 Hello #{name},而不 是 Hello Fred)。 不仅仅是变量可以嵌入到双引号界定字符串中。你也可以将非打印字符如”n”和”t”嵌 入进去。你甚至可以在双引号字符串中嵌入一小段代码和数学表达式。假设你已经有了一个 名为 showname 的方法,方法返回字符串”Fred”。 下面的字符,在求值的过程中将会调用 showname 方法,结果是打印字符串”Hello Fred”: puts “Hello #{showname}” 看看你能不能算出下面的代码将打印出什么: puts(“nt#{(1+2)*3}nGoodbye”) 3string_eval.rb 好的,现在运行 3string_eval.rb 程序,看看你是不是对的。 数字 数字的使用就和字符串一样简单。假设你要通过一些款项的税前价格或者小计来计算其销售 价格或者总计金额,你需要将小计与相应的税率相乘,然后再加上小计值。假设小计为 100,税率为 17.5%,以下 Ruby 程序将进行计算并打印其结果: 4calctax.rb subtotal = 100.00 taxrate = 0.175 tax = subtotal * taxrate
  • 6. puts “Tax on $#{subtotal} is $#{tax},so grand total is $#{subtotal+tax}” 显然,如果这个程序能计算不同种类的小计而不只是一遍遍地计算同样的值会更加有用。这 里有一个简单的版本,会提示用户输入小计值: taxrate = 0.175 print "Enter price (ex tax): " s = gets subtotal = s.to_f tax = subtotal * taxrate puts "Tax on $#{subtotal} is $#{tax},so grand total is $#{subtotal+tax}" 这里的 s.to_f 是 String 类的一个方法。它将 string 转化为浮点数。比如,字符串 ”145.45”将会转化为浮点数 145.45。如果该字符串不能被转化,返回 0.0。所以, ”Hello world”.to_f 将返回 0.0。 注释 ... 随这本书发布的很多源码都有很多的注释文档,这些注释文档是被 Ruby 解 释器忽略的。 注释一般都是在英镑 ( 哈希 ) 符号#之后。在该字符之后的同一行文本均为 注释: #this is a comment Puts(“hello”) #this is also a comment 如果你想书写多行注释,你可以在多行文本的开头加上 =begin 在结尾加上 =end (=begin 和= end 必须左对齐 ): =begin This is a Multiline Comment =end 条件 判断语 句:IF … THEN 上面的那个简单的税收计算器存在一个问题就是它接受负的小计值并计算出负的税收—这种 状况可是政府部门不愿意看到的!因此需要检查负值,如果出现负值将其设置为 0。这是我 5taxcalculator.rb
  • 7. 新版本的代码: taxrate = 0.175 print "Enter price (ex tax): " s = gets subtotal = s.to_f if (subtotal < 0.0) then subtotal = 0.0 end tax = subtotal * taxrate puts "Tax on $#{subtotal} is $#{tax},so grand total is $#{subtotal+tax}" Ruby 中的条件判断和其他语法中相似。需要注意的是,括号仍然是可选的,而且关键词 then 可是可选的。然而,如果你打算像下面那般书写代码,在条件判断后面不换行,那么 then 是必须的: if(subtotal < 0.0) then subtotal = 0.0 end 这样将所有的代码写在一行不能给代码增加任何可读性,我更倾向于防止这样写代码。由于 我长时间的 Pascal 编码经历,我会本能地在 if 条件后添加一个 then ,而这个真的是没有必 要,你可以认为这是我个人的一点点小偏执。关键词 end 用于结束 if 语句块,它是必须的。 忘记了添加 end 你的代码将无法运行。 局部变量和全局变量 在上一个例子中,我给 subutotal,tax 和 taxrate 等变量进行了赋值。这些变量均以小写字 母开头,它们都是局部变量。这就意味着它们只是存在于程序的某一指定部分,也就是说, 它们受限于一个定义明确的范围。下面是一个例子: variables.rb localvar = “hello” $globalvar = “goodbye” def amethod localvar = 10
  • 8. puts( localvar ) puts( $globalvar ) end def anotherMethod localvar = 500 $globalvar = “bonjour” puts( localvar) puts( $globalvar ) end 在例子中有三个局部变量名为 localvar。一个在程序的“main 范围”赋值为”hello world”,另外两个都是在单独的方法中赋值:因为每个局部变量所处的范围不同,对任意 一个变量赋值都不会影响到其他范围中同名变量的值。你可以通过依序调用这些方法来验证 一下: amethod #=>localvar = 10 anotherMethod #=>localvar = 500 amethod #=>localvar = 10 puts( localvar ) #=>localvar = “hello” 而另一方面,全局变量—以美元符$开头的—拥有全局范围。当我们在一个方法中对全局变 量进行赋值,将会影响到其在程序别处的值: amethod #=>$globalvar = “goodbye” anotherMethod #=>$globalvar = “bonjour” amethod #=>$globalvar = “bonjour” puts($globalvar) #=>$globalvar = “bonjour” 类和对象 我们本应继续深入到 Ruby 的语法中,去了解它的类型、循环、模块等等(不用担心,我们 之后还会回到这些概念的),然而我想我们应该尽可能快地了解一下如何创建类和对象。 类、对象和模块 类是对象的蓝图。它定义了一个对象包含的数据和它的行为方式。许多不同的对 象可以通过一个类创建。所以你可能只有一个 Cat 类,但是会有三个 cat 对 象 :tiddles,cuddles 和 flossy 。方法 ( 函数 ) 或者子程序都在类内部定义。
  • 9. 宣称 Ruby 是面向对象的看起来不见得就有多了不起啊。这段时间不是所有的语言都宣称是 面向对象的吗?对,一针见血。现代大多数的“面向对象”编程语言(Java,C++,Object Pascal 等等)都或多或少的表现出了一定程度上的 OOP(Object Oriented Programming)的 特性。然而 Ruby 是完全面向对象的。事实上,除非你曾经使用过 Smalltalk 或者 Eiffel(比 Ruby 更面向对象)进行过开发,它差不多是你用过的编程语言中最为面向对象的。每一种数 据—从简单的数字和字符串到复杂的文件和模块--在 Ruby 中均为对象。并且对类的操作几 乎都是通过方法来完成的。即使像”+”和“-”这样的算术运算符都是方法。思考下面的 代码: x=1+2 这里的”+”是一个整型数对象 1 的方法。数值 2 传递给该方法,返回结果为 3,将结果赋 值给对象 x。然而,“=”运算符却是“类的操作均为方法”原则外极少数的异类。这个赋 值运算符是一个特殊内建机制(这不是正式的术语,只是我自己加上的)不是任何类的方法。 现在让我们来看看如何创建一个自己的对象。和大部分 OOP 语言一样,Ruby 的对象也是 通过类来定义的。类就像即将创建的对象的蓝图一般。下面的例子定义了一个 Dog 类: class Dog def set_name(aName) @myname = aName end end 类的定义由关键字 class(均小写)开始,后跟类名,类名必须以大写字母开头。这个类有一个 方法为 set_name。它有一个传入参数,aName。方法内部将该参数 aName 的值赋给了一 个名为@myname 的变量。 实例变量 由@字符开头的变量是”实例变量”--这意味着他们隶属于单个对象--或者说是类的实例。 实例变量也不需要预声明。我可以通过 new 方法创建 Dog 类的一个实例(即 Dog 对象)。在 这里,我创建两个 Dog 类的实例(注意:类名以大写字母开头而对象名以小写字母开头): mydog = Dog.new
  • 10. yourdog = Dog.new 现在这两条狗还没有名字呢,所以下一步要做的就是给这两条狗起名字,调用 set_name 方 法给它们起名: mydog.set_name('Fido') yourdog.set_name(’Bonzo') 已经给每条狗都起了一个名字,然后我需要什么方法来查看他们的名字。我该怎么办呢?我 不能到一个对象的内部去获取@name 变量,因为每一个对象的内部细节只有对象自己知道。 这是“纯”面向对象的基本原则:每个对象内部的数据是私有的。进出对象的方法是明确定 义的。只有对象自身可以更改其内部的状态。外部世界对此无能为力。这称为“数据隐藏”, 是“封装”原则的一部分。 封装: 在 ruby 中,封装并不像它最初那般严格要求。有一些相当恶心的陷阱,让你能够在对 象内部为所欲为。为清楚起见 ( 也为了确保你和我不会做噩梦 ) ,我现在先掠过 Ruby 的这些特性。 因为我们需要每条狗都知道它们自己的名字,所以让我们给 Dog 类提供一个 get_name 方 法: def get_name return @myname end return 关键词在这里不是必须的。当省略 return 的时候,Ruby 方法会返回方法体内最后一 个表达式的值。 为了清楚起见(也为了防止比这个方法复杂的方法返回意外值),我习惯于显式地的返回我计 划使用的值。 最后,让我们给这些狗们添加一些行为,比如说让它叫。这是完整类的定义: class Dog def set_name( aName ) @myname = aName end
  • 11. def get_name return @myname end def talk return 'woof!' end end 现在,我们可以创建一个 Dog 的实例了,给他命名,并显示它的名字,让它叫: mydog = Dog.new mydog.set_name('Fido') puts(mydog.get_name) puts(mydog.talk) 6dogs.rb 我在 6dogs.rb 中写了一个上面代码的扩展版。在程序中还包含了一个跟 Dog 类似的 Cat 类, 只是在它们的 talk 方法上存在一定的差异,只是返回”miaow”来代替”woof”。 喔!这个程序好像有错误。 名为 someotherdog 的对象的 @name 变量从未被赋值。幸运的是, Ruby 并 不会在我们试图打印这条狗的名字时候发飙。相反的是,它只是打印出 'nil' 。我 们将快速地看看保证这种错误不再发生的简单方法。 消息,方法和多态 这个例子是基于一个经典的 Smalltalk 的 demo 程序,它描述了相同的“消息”(如 talk 方 法)可以传递给不同的对象(如 cats 和 dogs),针对同一个消息,对象调用自身特定的方法 (这里是 talk 方法)反应却不相同。不同的类拥有同名的方法在面向对象中称为”多态”--一 个一旦记住就很难忘掉的词。 当你在运行一个类似 6dongs.rb 的程序时,代码是依序执行的。这些类的代码只在程序下 方中创建对象的代码之后才执行。你将看到我经常是把类的定义和在程序运行时独立的代码
  • 12. 段混搭着写。这也许不是你写一些重要应用程序时的选择,但是在你想测试一些东西的时候 就是非常便利的了。 独立代码段 ...? 如果 Ruby 是一个真正的面向对象的语言,你可能会觉得可以创建独立的悬挂方法是奇怪的。 但事实上,当我们在运行一个程序的时候, Ruby 创建了一个 main 对象以及其他所有在 同一个代码单元中的代码,不论是什么形式的代码,根本就是不什么独立悬挂的代码,实际 上都是运行在 main 对象内。你可以很容易的验证这一点。创建一个新的源文件,添加下面 的代码,然后运行,查看其输出: put self puts self.class 这个程序的明显的缺点就是那两个类,Cat 和 Dog 太重复了。如果只有一个 Animal 类,它 有 get_name 和 set_name 方法的话,并由此派生两个类 Cat 和 Dog,那就有意义多了。 在 Cat 和 Dog 类中分别包含各自的方法 woofing 或者 miaowing,针对不同的动物设置不 同的行为。我们将在下一章探讨如何实现。 构造器—创建和初始化 现在,让我们来看看另外一个用户自定义类的例子。加载 7treasure.rb。这是一个正在制作 中的冒险游戏。它包含了两个类,Thing 和 Treasure。Thing 类和上一个程序中的 Cat,Dog 类很相似,区别就是它没有 woof 或者 miaow 的行为。 Treasure 类没有 get_name 和 set_name 方法。它包含一个名为 initialize 的方法,该方法 有两个参数,分别赋值给@name 和@description 变量。 7treasure.rb def initialize(aName, aDescription) @name = aName @description = aDescription end 当一个类包含了一个名为 initialize 方法的时候,当对象是通过 new 方法创建的时候将自动 调用该方法。使用 initialize 方法来设置对象实例变量的值是一个好主意。 这比使用 set_name 这样的方法来设置每一个实例变量值很显然多了两个好处。首先,一个 拥有众多实例变量的类,可以通过这一个 initialize 方法来初始化所有的变量的值,而不需 要通过众多的独立”set”方法来给这些变量赋值;其次,如果所有的变量在对象创建的时 候就自动初始化好了,你将永远不会因为一个“空”值(就像在我们在上一个程序中试图打
  • 13. 印 someotherdog 的名字时返回 nil 值)而出错。 最后,我创建了一个方法 to_s 用来返回一个 Treasure 对象的字符串描述。这个方法名 to_s 不是随便起的。与之同名的方法在 Ruby 标准对象的整个层次结构中都在使用。事实上, to_s 方法是为 Ruby 中所有类的终极祖先 Object 类定义的。通过重定义 to_s 方法,我添加 了一些比默认方法更适合于 Treasure 类的行为。也就是说,我重载了它的 to_s 方法。 new 方法创建对象,所以可以将其视为对象的构造器。然而,一般不能实现你自己的 new 方法(这是可能的,但是并不建议这么做)。如果你想执行任何”setup”行为—比如说赋值 给对象的实例变量,你可以在 initialize 方法中做。Ruby 将会在对象创建之后立刻调用 initialize 方法。 垃圾回收: 在很多语言中,如 C++ 和 Delphi(Win32) ,它是需要程序员负责销毁其创建后不再使 用的对象。也就是说,对象同时需要析构器和构造器。在 Ruby 中,你不需要这么做, 因为 Ruby 有一个内置的“垃圾回收器”将在对象不在被你的程序引用的时候自动销毁 对象并回收它们使用到的内存。 检测对象 另外,大家可能也注意到了我查看了 Treasure 对象 t1 的内部结构,通过 inspect 方法: t1.inspect 所有的 Ruby 对象均定义了 inspect 方法。它返回一个关对对象的易读的字符串。在这个例 子中,它打印的信息如下: #<Treasure:0x28962f8 @description="an Elvish weapon forged of gold", @name="Sword"> 它以类名 Treasure 开头,随后是一个数字,可能跟上面的不一样--这是 Ruby 内部针对 这个对象的识别码;然后是对象的变量名和变量的值。 Ruby 通过提供了一个快捷的方法来检测对象并将起详细信息打印出来,这个方法就是 p: p.rb p(anobject) 运行 8to_s.rb,查看 to_s 怎么可以由这么多的对象调用,测试一个 Treasure 对象怎么在不 重载 to_s 方法的情况下,将对象转化为一个字符串。 8to_s.rb
  • 14. puts(Class.to_s) #=> Class puts(Object.to_s) #=> Object puts(String.to_s) #=> String puts(100.to_s) #=> 100 puts(Treasure.to_s) #=> Treasure 你将会看到,像 Class,Object,String 和 Treasure 这些类,在调用 to_s 方法时都会返回它 们的名字。像 Treasure 这样的对象 t,还将会返回它的标识码--和 inspect 方法返回的识 别码是相同的: t = Treasure.new( "Sword", "A lovely Elvish weapon" ) puts(t.to_s) #=> #<Treasure:0x3308100> puts(t.inspect) #=> #<Treasure:0x3308100 @name="Sword", @description="A lovely Elvish weapon"> 虽然 7treasure.rb 给一个包含众多不同对象的游戏打下了基础,但是它的代码重复性依然太 高(冗余性过高)。毕竟,为何 Thing 类和 Treasure 类都包含一个名字呢?将 Treasure 视为 Thing 的一种岂不更有意义?在一个完整的游戏中,其他的对象,例如 Rooms 和 Weapons 可以是其他类型的 Thing。是时候开始进入到类继承的内容了,下章我们将开始...