第1章           类与模块

要建立新的对象,必须使用每个类的new方法。

arr = Array.new

print arr          #=> []

数组与字符串这类内建的类,很多都能以字面常数(以语法的形式提供的[1,2,3]、”abc”这类写法)的方式来建立对象。

 

想知道对象属于哪个类,可用class方法。

str = “Hello world!”

str.class           #=> String

要判断一个对象是否是某个类的实例,可用

instance_of?方法。

arr = []

print.instance_of?(Array)

用is_a?方法,可以检查有继承关系的父类

str = “This is a String.”

p str.is_a?(Object)       #=> true

 

自己定义类

class HelloWorld

  def initialize(myname = “Ruby”)

@name = myname

  end

  def hello

print “Hello, world. I am”, @name, “.\n”

  end

end

bob = HelloWorld.new(“Bob”)

ruby = HelloWorld.new

bob.hello

定义类时要使用class语句,其一般形式为:

class 类名      # 类名一定要以大写字母开始

  类定义

end

 

调用new方法建立对象时,initialize方法会被调用。

 

以@开始的变量称为实例变量。与局部变量不同,实例变量的值在离开方法以后也仍然存在。

 

访问方法

用来设定实例变量的方法称为setter,而用来读取实例变量的方法称为getter。这两种方法放在一起,合称为访问方法(accessors)。

class HelloWorld

   …

   def name

      return @name

   end

   def name=(value)

     @name = value

   end

end

Ruby不允许从对象外部直接读取、写入实例变量。所以要访问对象内部的数据,都需要定义方法来操作。

 

定义访问方法

定义

意义

attr_reader :name

只定义用来读取的方法 (定义name方法)

attr_writer :name

只定义用来写入的方法(定义name=方法)

attr_accessor :name

定义用来读取与写入的方法 (定义上述两种方法)

 

3种方式定义类方法

写成“def 类名.方法名 ~end”

class HelloWorld

  def HelloWorld.hello(name)

     print name, “said Hello.”  #这里name是局部变量

  end

end

HelloWorld.hello(“John”)

写成“class<< 类名~def 方法名 ~end end”

class HelloWorld

..

end

class << HelloWorld

  def hello(name)

     print name, “said Hello.”

  end

end

HelloWorld.hello(“John”)

写成“class 类名~def self.方法名 ~ end end”

class HelloWorld

  def self.hello(name)

print name, “said Hello.”

  end

end

HelloWorld.hello(“John”)

 

类方法的表示采用如Array.new或Array::new的形式。

 

类内的常数可以使用“::”连接类与常数名称,从类外部访问。

class HelloWorld

   Version = “1.0”

   ..

end

p HelloWorld::Version     #=> “1.0”

类变量

以“@@”开始的变量称为类变量,类变量是该类所有实例共用的变量。虽然类似于常数,但变量值是可以修改的。另外,要从类外部访问类变量,必须自己定义访问方法。

 

扩充类

class String

  def count_word

     ary = self.split(/\s+/)

     return ary.size

  end

end

str = “Just Another Ruby Newbie.”

p str.count_word        #=> 4

要实现这个功能,需要在count_word方法的定义中能够去引用count_word方法被调用时那个作为接收者的对象。这个接收者,可以从变量self取得。

 

使用继承

class 类名 < 父类名

   类定义

end

super方法用来调用父类中同名的方法。

 

限制方法的调用

Ruby提供了3种限制层级:

l  public --- 将方法公开为外部可以使用的实例方法;

l  private --- 将方法限制为只有内部可以使用(不允许接在接收者后面调用);

l  protected --- 将方法限制为只有内部可以使用。另外,在同一个类中可以作为实例方法使用。protected方法可以从同一个类(与其子类)中以实例方法的方式调用,但无法从其他地方调用。

class AccTest

  def pub

puts “pub is a public method.”

  end

  public :pub          #=> 将pub方法设定为public (不指定意义也一样)

  def priv

puts “priv is a private method.”

  end

  private :priv         #=> 将priv方法设定为private

end

acc = AccTest.new

acc.pub              #=> pub is a public method.

acc.priv              #=> 错误

   想要一口气对很多方法做相同的访问限制时,可以这样写:

class AccTest

  public           #=> 没有指定实参的话,下面定义的所有方法都是public

  def pub

     puts “pub is a public method.”

  end

  private

  def priv

     puts “priv is a private method.”

  end

end

没有特别指定的方法都是public,但initialize方法例外,它恒为private。

 

Duck Typing (行为决定类型)

对象的特征并不是依照其实际的种类(类),而是依照该对象具有什么行为(具有哪些方法)来决定的。

 

Ruby的变量没有类型,所以程序要在执行阶段才能判断变量所指向的对象是否有某个方法可以调用。

 

模块(module)

模块是Ruby所特有的功能之一。类用来表现具有数据与行为(程序)的“东西”,而模块大致来说,则是只有程序部分的集合体。类与模块最大的不同在于:

l  模块不能建立实例

l  模块不能继承

 

模块的用法

(1)     提供命名空间

以模块形式提供的方法必须使用“模块名.方法名”的方式调用。以这种形式调用的方法又称为模块函数。

当模块内部定义的方法及常数的名称没有与现在命名空间冲突时,省略模块名称会比较方便。使用include可以将模块所拥有的方法名称和常数名称读入现在的命名空间里。例如:

p Math::PI

p Math.sqrt(2)

include(2) Math

p PI

p sqrt

 

(2)     以Mix-in方式提供功能

将模块混进类里,称为“Mix-in”。在类的定义内使用include,可以将模块里所以定义的方法与常数纳入类定义里。

 

自己定义模块

module 模块名

  模块定义

end

模块名一定要以大写字母开头。

module HelloWorld

  Version = “1.0”               #=> 定义常数

  def hello(name)              #=> 定义方法

print “Hello, ”, name, “.\n”

  end

  module_function :hello        #=> 将hello以模块函数形式公开

end

p HelloWorld::Version           #=> “1.0”

HelloWorld::hello(“Alice”)        #=> “Hello, Alice.”

与类一样,module内部也可以定义方法。只是方法在定义以后,只能在模块内部调用,不能使用“模块名.方法名”的方式调用。要将方法对模块外部公开,必须使用module_function。module_function的实参是想要公开的方法名称的符号。

 

在模块函数内引用self (接收者),可以取得该模块本身。这样就可以使用模块函数去改写模块本身的状态了,但一般来说模块函数通常不会去使用self。

另外,在类中使用Mix-in功能的目的是为该类增加实例方法。这时self会是Mix-in目标类的实例。