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

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

Ruby Study 2 : CLASS HIERARCHIES,  ATTRIBUTES, AND CLASS  VARIABLES - 德哥@Digoal - The Heart,The World.
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
目录
相关文章
|
移动开发 关系型数据库 Ruby
|
6月前
|
JSON 数据格式 Ruby