ruby学习笔记-4 class&module
Class和Module是Ruby中的两个重要概念。作为一个纯OO语言,class的概念自然容易理解,即为object的抽象;而module则明显有别于其它语言地提供了mixin方法来解决多继承缺失带来的不便 - 集成多个基类的接口并维持IS-A关系以及LSP替换.
相互关系和一些基本类
ruby中存在一些基础的类(或者是MetaClass),包括: [Class, Module, Kernel, Object, BasicObject]
, 且看如下测试:
tests = [Class, Module, Kernel, Object, BasicObject]
puts "checking class and ancestors for #{tests}"
tests.each do |x|
puts "Ancestors of #{x} is #{x.ancestors}"
puts "The class of #{x} is #{x.class}"
end
puts ""
def check_is_a(a, b)
if not a.is_a? b
puts "#{a} is not a #{b}"
end
end
puts "checking is_a relation for #{tests}"
tests.each do |x|
tests.each do |y|
check_is_a(x, y)
end
end
输出结果如下:
checking class and ancestors for [Class, Module, Kernel, Object, BasicObject]
Ancestors of Class is [Class, Module, Object, Kernel, BasicObject]
The class of Class is Class
Ancestors of Module is [Module, Object, Kernel, BasicObject]
The class of Module is Class
Ancestors of Kernel is [Kernel]
The class of Kernel is Module
Ancestors of Object is [Object, Kernel, BasicObject]
The class of Object is Class
Ancestors of BasicObject is [BasicObject]
The class of BasicObject is Class
checking is_a relation for [Class, Module, Kernel, Object, BasicObject]
Kernel is not a Class
可见,Kernel本身是个Module,但不是一个Class,其它的都互相满足is-a关系。其它任何一个class都是一个module(但是并不意味着可以include class)。
构造测试
Module不能用new来生成一个对象,譬如:
module TestModule
def func()
puts "value is @value"
end
end
begin
obj = TestModule.new
obj.func()
rescue Exception => e
puts "Module new got an exception: #{e.class}"
end
这里会抛出NoMethodError
异常。
MIXIN
Module的主要作用就是实现MIXIN。通过Module,某个class可以通过include
某个module来包含其所定义的方法。module起的作用类似于抽象基类,所有module定义的方法可以被子class调用或者重写。
下边的这段代码是一个简单的MIXIN例子:
class BaseClass
def call_func()
puts "base called"
end
end
class MixInClass < BaseClass
include TestModule
def initialize(value)
@value = value
end
def call_func()
super
puts "called in child"
end
def func()
super
puts "module func called"
end
end
obj = MixInClass.new("mixin")
obj.func()
# module method called
obj.call_func()
# virtual method called which will call it's ancestor by super
上述代码中,super
用于调用BaseClass
或者对应Module中的方法。
方法冲突的解决
通过MIXIN,一个Class可以通过include来MIXIN多个module的方法。如果有两个module中存在同名的方法,行为又会如何?下边是一个例子:
module BaseModule1
def func()
puts "called in BaseModule1"
end
def func1()
puts "unique func in module1"
end
end
module BaseModule2
def func()
puts "called in BaseModule2"
end
def func2()
puts "unique func in module2"
end
end
class BaseClass
def func()
puts "called in base class"
end
end
class Child < BaseClass
include BaseModule1
include BaseModule2
end
obj = Child.new
obj.func
obj.func1
运行结果如下:
called in BaseModule2
unique func in module1
unique func in module2
从而可得如下结论:
- 同名的方法,优先选择module中的定义
- 如果有module方法名字冲突,ruby选择最近的一个include的module的实现
- 由于不可能集成多个base class,就不可能出现在base class冲突的情况
initialize方法处理
module的initialize方法处理规则有些特殊,如下例:
module BaseModule
def initialize(value)
@value = value
end
def func()
puts "called with value = #{@value}"
end
end
class Test1
include BaseModule
def initialize(value)
super(value)
end
end
class Test2
include BaseModule
def show()
puts "my value is: #{@value}"
end
end
class Test3
include BaseModule
def initialize(value)
@myvalue = value
end
def show()
puts "called with myvalue = #{@myvalue}"
end
end
obj1 = Test1.new("test1")
obj1.func()
obj2 = Test2.new("test2")
obj2.func()
obj2.show()
obj3 = Test3.new("test3")
obj3.func()
obj3.show()
运行结果如下:
called with value = test1
called with value = test2
my value is: test2
called with value =
called with myvalue = test3
这里有三种处理情况:
- 包含module的class没有定义
initialize
函数,module的initialize
函数会被调用 - class中定义了
initialize
,并且其中调用了super
,module的initialize
函数会被调用 - class虽然定义了
initialize
,但是没有调用super
,则module的initialize
不会别调到
这里还有另外一种情况即如果class的BaseClass也定义了initialize,如下述代码:
module BaseModule
def initialize(value)
@value = value
end
def func()
puts "called with value = #{@value}"
end
end
class BaseClass
def initialize(value)
@baseValue = value
end
def show()
puts "my basevalue=#{@baseValue}"
end
end
class Test < BaseClass
include BaseModule
def initialize(value)
super(value)
end
end
obj = Test.new("test1")
obj.func
obj.show
结果如下:
called with value = test1
my basevalue=
这里,module的initialize
方法有较高的优先级,即super
调用会首先调用module的同名函数。
Leave a Comment
Your email address will not be published. Required fields are marked *