1. Class Hierarchies
熟悉PostgreSQL的朋友一定深知它的inherit表. 在Ruby中也有类似的结构, 例如前一篇BLOG最后的代码部分有举例.
如下 :
![Ruby Study 2 : CLASS HIERARCHIES, ATTRIBUTES, AND CLASS VARIABLES - 德哥@Digoal - The Heart,The World.](http://img6.ph.126.net/_pVNp84DF2bBhhy6eBJ0cA==/1176002452714405746.png)
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等.
从一个书本上的例子可以得到验证 :
执行结果 :
2. Superclasses and Subclasses
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
我们注意到上面的写法还是太繁琐, 下面来看一个更简单的例子 :
执行结果 :
注意attr_writer和attr_reader方法会自动创建对应名字的实例变量, 即使在initialize方法中没有传入.如下 :
执行结果 :
另外, 使用非标准的attr_reader举例, 例如需要在获取实例变量时需要转换成大写 :
执行结果 :
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 :
执行结果 :
查看Student的类方法 :
8. The Root of All Classes
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
块内.
因此 :
等同于 :
正因为这样, RUBY的扩展性很强, 例如系统自带的Array类, 我想给它加个方法, 如下 :
输出 :
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