Ruby Study 2 : CLASS HIERARCHIES, ATTRIBUTES, AND CLASS VARIABLES

1. Class Hierarchies

熟悉PostgreSQL的朋友一定深知它的inherit表. 在Ruby中也有类似的结构, 例如前一篇BLOG最后的代码部分有举例.

如下 :

OtherClass 继承自Object

Object继承自BasicObject

Class继承自Module

Module继承自Object

我们可以从以下代码验证 :

class Myclass
end
class Mysubclass < Myclass
end

puts(Mysubclass.superclass) # superclass方法用于查找超类. 查找Mysubclass的超类
puts(Myclass.superclass) # superclass方法用于查找超类. 查找Myclass的超类
puts(Class.superclass)  # 查找Class的超类
puts(Module.superclass)
puts(Object.class)
p(BasicObject.superclass) # BasicObject没有超类, 可以认为是ROOT Class, 1.9的Object和BasicObject是从1.8的Object拆分而来. 1.8中没有BasicObject.

执行结果 : 

Myclass
Object
Module
Object
Class
nil

subclass继承了superclass的attribute和method. 并且subclass可以复写superclass的方法. 新增superclass中不存在的方法和attribute等.

从一个书本上的例子可以得到验证 :

class Thing  # 本例中Thing是Treasure的superclass
    def initialize( aName, aDescription )
      @name         = aName
      @description  = aDescription
    end
    def get_name
        return @name
    end

    def set_name( aName )
        @name = aName
    end

    def get_description
        return @description
    end

    def set_description( aDescription )
        @description = aDescription
    end
end

class Treasure < Thing   # 定义Treasure类继承自Thing
    def initialize( aName, aDescription, aValue )  # 复写superclass的initialize
        super( aName, aDescription )   # 调用superclass的同名方法(initialize).super表示传递所有参数调用superclass的同名方法, super()表示不传递任何参数调用superclass的同名方法, super(aName, aDescription) 表示传递两个参数调用superclass的同名方法.
        @value = aValue  # 复写的initialize方法中新增了一个实例变量@value.
    end

    def get_value   # 比superclass新增的方法.
        return @value
    end

    def set_value( aValue )  # 比superclass新增的方法.
        @value = aValue
    end
end

thing = Thing.new('digoal','a lucky dba')
treasure = Treasure.new('digoal','a lucky dba','1983')
puts(thing.inspect)
puts(treasure.inspect)
treasure.set_description('DBA') # 调用superclass中的method
puts(treasure.inspect) # 查看@description是否更改.

执行结果 : 

#<Thing:0x1b46f68 @name="digoal", @description="a lucky dba">
#<Treasure:0x1b46e30 @name="digoal", @description="a lucky dba", @value="1983">
#<Treasure:0x1426a40 @name="digoal", @description="DBA", @value="1983">

2. Superclasses and Subclasses

在第一点中已经讲得比较详细, 下面再举一个super的例子.

class A
  def to_s
    puts("it's class A")
  end
  def amethod
    puts("it's class A's amethod")
  end
end
class B < A
  def to_s
    super()  # super在这里将执行A中的to_s方法.
    puts("it's class B")
  end
end
class C < B
  def to_s
    puts("it's class C")
  end
  def amethod
    super()  # super在这里将执行A中的amethod方法. 因为C继承自B, B继承自A. 虽然B中没有定义amethod方法, 但是从A中继承了. 所以B的实例可以调用amethod, C的实例也可以调用amethod.
  end
end

b = B.new
b.to_s
b.amethod
puts("next..")
c = C.new
c.to_s
c.amethod

执行结果  :

it's class A
it's class B
it's class A's amethod
next..
it's class C
it's class A's amethod

3. Passing Attribute value to Superclass

这个在前面的例子也讲到了, 传递attribute给superclass.

实际上就是在subclass的initialize方法中调用super方法.

例如 :

# This passes a, b, c to the superclass
def initialize( a, b, c, d, e, f )
   super( a, b, c )
end
# This passes a, b, c to the superclass
def initialize( a, b, c )
   super
end
# This passes no arguments to the superclass
def initialize( a, b, c)
   super()
end

4. Accessor Methods

前面多个例子中都提到了对实例的变量进行初始化, 获取实例变量以及修改实例变量的值. 

但是由于实例变量只允许在实例内访问, 不能直接从外面访问. 所以每次获取和修改实例变量都需要写对应的类方法来实现.

下面我们来看个例子, 如何更加简洁的来获取和修改实例变量的值.

class A
  def initialize(a,b)
    @x = a
    @y = b
  end
  def x # 定义一个与实例变量x同名的方法
    return @x
  end
  def x=(a) # 定义一个包含实例变量,x= 的方法. 注意x=之间没有空格.
    @x = a
  end
  def y
    return @y
  end
  def y=(a)
    @y = a
  end
end

a = A.new('digoal','zhou')
p a
a.x = 'DIGOAL' # 使用a.x = 对@x进行修改或赋值.
a.y = 'ZHOU'
puts(a.x) # 使用a.x 获取@x的返回值.
puts(a.y)

执行结果 :

#<A:0x757ab8 @x="digoal", @y="zhou">
DIGOAL
ZHOU

5. Attribute Readers and Writers

我们注意到上面的写法还是太繁琐, 下面来看一个更简单的例子 :

class A
  def initialize(a,b)
    @x = a
    @y = b
  end
  attr_reader( :x, :y ) # attr_reader方法, 参数是Symbol, 与实例变量同名的Symbol. 相当于前面例子中定义的x,y方法.
  attr_writer( :x, :y ) # attr_writer方法, 参数是Symbol, 与实例变量同名的Symbol. 相当于前面例子中定义的x=,y=方法. 如果这里是attr_writer( :x ), 那么就没有定义y=方法.
end

a = A.new('digoal','zhou')
p a
a.x = 'DIGOAL'
a.y = 'ZHOU'
puts(a.x)
puts(a.y)

执行结果 : 

#<A:0x1ee0340 @x="digoal", @y="zhou">
DIGOAL
ZHOU

注意attr_writer和attr_reader方法会自动创建对应名字的实例变量, 即使在initialize方法中没有传入.如下 :

class A
  def initialize(a,b)
    @x = a
    @y = b
  end
  attr_reader( :x, :y, :z )
  attr_writer( :x, :y, :z )  # 调用 z= 方法后将创建@z变量.
end

a = A.new('digoal','zhou')
p a
a.x = 'DIGOAL'
a.y = 'ZHOU'
puts(a.x)
puts(a.y)
a.z = 'it\'s Z'
puts(a.z)
p a

执行结果 : 

#<A:0x1ab7fb8 @x="digoal", @y="zhou">
DIGOAL
ZHOU
it's Z
#<A:0x1ab7fb8 @x="DIGOAL", @y="ZHOU", @z="it's Z">

另外, 使用非标准的attr_reader举例, 例如需要在获取实例变量时需要转换成大写 :

class A
  def initialize(a,b)
    @x = a
    @y = b
  end
  attr_reader( :x )  # 这里未传入 :y Symbol.
  attr_writer( :x, :y )
  def y  # y方法用于输出首字母大写的@y
    return @y.capitalize
  end

end

a = A.new('digoal','zhou')
p a
puts(a.y)

执行结果 :

#<A:0x1da0348 @x="digoal", @y="zhou">
Zhou

但是使用attr_reader和attr_writer还是闲麻烦怎么办.

还可以有更方便的吗?

如下 :

class A
  def initialize(a,b)
    @x = a
    @y = b
  end
  attr_accessor( :x, :y )
end

a = A.new('digoal','zhou')
p a
puts(a.x)
puts(a.y)

执行结果 : 

#<A:0x1e50508 @x="digoal", @y="zhou">
digoal
zhou

attr_accessor 相当于同时执行了attr_reader和attr_writer

6. Calling Methods of a Superclass

这个在前面已经列举过了, 在subclass的方法中, 使用super方法调用superclass的同名方法.

7. Class Variables

前面我们接触到了localvar, globalvar, instance variable.

接下来引入class variable, 以@@开头.

例如我们有一个Student类, 每新增一个同学班上的同学总数就加1 :

class Student
  @@count = 0  # 定义一个class variable, 初始值=0
  def initialize(a,b)
    @@count += 1  # 每次调用initialize方法时, @@count自增1.
    @firstname = a
    @lastname = b
  end
  def self.count # 定义一个属于Student这个类的方法.
    return @@count
  end
  def count # 定义一个instance 方法. 这两个方法虽然都叫count, 但是一个只能用Student调用, 一个只能在新建了Student实例后通过实例调用, 以后会将到这个. 我们可以定义属于类的方法, 还可以定义属于对象的方法. 通过Student.singleton_methods可以查看它的类方法.
    return @@count
  end
end

puts( Student.count )
a = Student.new('Zhou','Digoal')
puts( Student.count ) # 通过Student类调用count方法
puts( a.count ) # 通过a对象调用count方法.
b = Student.new('Zhou','Digoal')
puts( Student.count )
puts( a.count )
puts( b.count )
c = Student.new('Zhou','Digoal')
puts( Student.count )
puts( a.count )
puts( b.count )
puts( c.count )

执行结果 : 

0
1
1
2
2
2
3
3
3
3

查看Student的类方法 :

puts( Student.singleton_methods )
返回
count

8. The Root of All Classes

最开始已经讲过了.

1.9 中所有类的根类都是BasicObject

1.8 则为Object.

9. Nested Class

内嵌class, 这个用法比较特殊, 但是可以说明一个问题, 通过 :: 可以调用内嵌类或常量.

举例 :

class A
  class NestedA # 这里定义了一个A的内嵌类NestedA
    def initialize(a) # 这里是内嵌类的内容
      @x = a
    end
    attr_accessor( :x )
  end
  def initialize(b) # 这里是A类的内容
    @y = b
  end
  attr_accessor( :y )
end

a = A::NestedA.new('digoal')  # 定义一个内嵌类的对象
p a
b = A.new('DIGOAL') # 非内嵌类的对象
p b

执行结果 : 

#<A::NestedA:0x1e40c98 @x="digoal">
#<A:0x1e40bc0 @y="DIGOAL">

10. Extend Class or Partial Class

注意, Ruby的Class的定义非常灵活, 所以扩展性非常强, Class的内容可以不需要写在一个

class A

end

块内.

因此 :

class A
  def initialize( a, b )
    @x = a
    @y = b
  end
end
# 这中间可以写其他代码, 如定义class B end. 没有关系.
class A
  attr_accessor( :x, :y )
end

等同于 : 

class A
  def initialize( a, b )
    @x = a
    @y = b
  end
  attr_accessor( :x, :y )
end

正因为这样, RUBY的扩展性很强, 例如系统自带的Array类, 我想给它加个方法, 如下 :

class Array
  def mymethod # 给Array类新增一个mymethod方法.
    puts('my Array\' mymethod')
  end
end

[1,2,3].mymethod

输出 : 

my Array' mymethod

11. Constants Inside Classes

常量以大写开头, 从外部访问也是使用 :: .

class A
  Count = 1
end

puts( A::Count )

输出 :

1

【参考】

The Book Of Ruby

Ruby 1.9.3 API

时间: 2024-12-21 20:26:28

Ruby Study 2 : CLASS HIERARCHIES, ATTRIBUTES, AND CLASS VARIABLES的相关文章

Ruby Study 13 : Modules and Mixins

在Ruby中class的superclass只能有一个, childclass可以有多个. 所以一个class如果要继承多个class的特性是不可能的. 如果有这种需求, 可以考虑把这些特性封装在Module中. Module可以很好的解决这类问题, 通过require或者load或者include可以把Module的instance method等带入到当前环境, 也就是可以使用Module中的方法等. 一个class可包含多个Module, 接下来详细的讲解. 1. A Module Is

Ruby Study 10 : Exception Handling

exception 在程序开发中是不可或缺的部分, 程序在使用过程中总会遇到不可预知的问题, 例如可预知的访问文件的程序可能在访问过程中文件被删除或移动等. 但是总会有不可预知的, 不可能在写程序时全部规避掉. 所以就有了exception handling. 在Ruby中exception 是Exception calss或者它的subclass的object. 下面来详细的讲解一下异常时如何捕获exception. 注意Ruby的Exception Handling分为几个层面 , 异常执行

Ruby Study 3 : String and Range class

1. Normal String Delimiter ( ' and " ) 前面两篇已经接触了很多String的例子, 下面再举一个, 注意看单引号和双引号的区别 : s1 = 'Hello' s2 = 'World' s3 = '#{s1} #{s2}' s4 = "#{s1} #{s2}" puts(s3) puts(s4) 执行结果 : #{s1} #{s2} Hello World 2. User Defined String Delimiter 当String中有

Ruby Study 8 : Methods

1. Class Methods 调用一个方法时, 可以通过定义这个方法的类的对象来调用, 那么这个称为实例方法. 还可以定义类方法, 这种方法的调用需要通过类来调用, 不能通过这个类的对象来调用. 其实我们创建的Class也是一个对象, 这个后面会讲到, 所以方法都是由对象来调用的. class MyClass def MyClass.classMethod # 定义class method可以使用这种写法, 后面还会介绍其他写法.class method也称为这个class的singleto

Ruby Study 12 : Symbols

本章主要讲解Symbol 类. 讲解Symbol比较重要的是大家需要了解object_id的知识. Symbol和Fixnum很相似, 同样的symbol它们的object_id是一致的, 而字符串则不是这样的, 举个例子来回忆一下: v1 = :name p v1 p v1.object_id p :name.object_id v2 = "name" p v2 p v2.object_id p "name".object_id v3 = 10 p v3 p v3

Ruby Study 6 : Loops and Iterators

前面多次介绍到了Array, Hash, Set, Range, Matrix, Vector 等collection class的Iterator特性, 如each方法. 这次专门讲一讲Loops和Iterators. 1. for loops for循环和其他编程语言类似, 看个例子 :  下面的例子中包含了,String, Array, Hash, Set, Range, Vector, Matrix的Iterator 操作以及each方法和for语法的转换. require "Matri

Ruby Study 7 : Conditional Statements

这篇的内容很简单, 基本上都是以前用过的语法, 稍微讲的详细一点. 1. if .. then .. else #!/opt/ruby/bin/ruby def get_daytype(aDay) if (aDay.to_s.downcase == 'saturday') || (aDay.to_s.downcase == 'sunday') # 分行写的话then可以忽略. daytype = 'weekend' else daytype = 'weekday' end return dayt

Ruby Study 4 : Array

Array class 1. introduce Ruby的Array类和其他的不太一样, Ruby中允许一个Array对象中存储不同类型的元素. 例如 : class Myclass end my1 = Myclass.new a1 = Array.new([1,"Digoal",[1,2,3,4,5],4,my1]) p a1 执行结果 :  [1, "Digoal", [1, 2, 3, 4, 5], 4, #<Myclass:0x1e11250>

Ruby Study 9 : Passing Arguments and Returning Values

1. Summarizing Instance, Class and Singleton methods Instance methods指的是可以被它所在的类的实例调用的方法. Class 方法和Singleton 方法指属于某个单独的对象的方法,不能被其他对象调用.(注意我把类方法也这么解释, 是因为类(包括BasicObject,Object,Module,Class以及自定义的类等)其实都是Class的对象.) 2. Returning Values 在很多编程语言中一般分有返回和不返回