模块的定义跟类非常相似,只是用module关键字取代class关键字而已。不过与类不同,模块不能被实例化,也不能被子类化。模块是独立的,在继承体系中没有任何所谓的“模块等级”。
模块对象是Module类的一个实例,所有的类都是模块,不过并非所有的模块都是类。
模块有些面向过程的感觉,有时候只需要一些方法,并不想定义类时,就可以使用模块。这时的模块主要起命名空间的作用。
Module Base64
def self.encode
end
def self.decode
end
end
其中self可以用当前的模块名替换。方法引用方式与类也是一样的
Base64.encode(text),常量也是用“模块名::常量名”的方式引用。
模块还有一种作用叫混入(mixin),如果一个模块定义了实例方法而非类方法,这些实例方法可以混入其它类中。Enumerable与Comparable是两个著名的混入模块。Enumberable模块定义了若干有用的迭代器,它们都基于each迭代器。Enumerable模块自身并没有定义each迭代器,但是如果混入Enumerable模块的类定义了迭代器,它就立刻具有这些强大的迭代器。Comparable也与此类似,它定义了一组比较操作符,后者都是基于通用比较符<=>的,如果你的类定义了<=>,那么混入Comparable模块后,它就能自动拥有<、<=、==、>、>=和between?这些操作符和方法。
想把一个模块混入一个类中, 我们必须使用include。include通常像一个关键字一样被使用:
class Point
include Comparable
end
实际上,include是Module类的一个私有实例方法,它隐式地被self调用---在这里就是包含模块的类。如果使用方法的形式,上面的代码变为:
class Point
include(Comparable)
end
include是私有方法,它必须以函数形式被调用,而不能写成self.include(Comparable)。include方法可以接受任意多的Module对象进行混入,因此一个定义了each和<=>的类可以加入下面的代码:
include Enumerable,Comparable
包含一个模块会影响is_a?这个类型检查方法及条件比较符===,在ruby1.8中,String混入了Comparable模块和Enumerable模块:
"text".is_a? Comparable #=>true
Enumerable==="text" #=>true in ruby 1.8,false in 1.9
注意instance_of?只对比接收者的类,而不管其超类或模块,所以下面的代码返回false:
"text".instance_of? Comparable #=>false
尽管每个类都是模块,但是include方法不允许把一类包含在另外一个类中,它的参数必须是用module进行声明的模块,而不能是类。
混入一个模块的普通方式是使用Module.include方法,另外一种方法则是使用Object.extend方法,这个方法使指定模块的实例方法成为接收对象的单键方法。(如果接收者对象是一个Class实例,那么这些方法就成为那个类的类方法)。如:
countdown=Object.new
#用来遍历
def coutdown.each
yield 3
yield 2
yield 1
end
#获得Enumerable的方法
countdown.extend(Enumerable)
print countdown.sort #prints "[1,2,3]"
感觉上面这段代码意思说得不是很明确,其实主要区别在网上看来的:
[quote] * include : mixes in specified module methods as instance methods in the target class
* extend : mixes in specified module methods as class methods in the target class[/quote]
module ReusableModule
def module_method
puts "Module Method: Hi there!"
end
end
class ClassThatIncludes
include ReusableModule
end
class ClassThatExtends
extend ReusableModule
end
puts "Include"
ClassThatIncludes.new.module_method # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method
有点不同的是,如果实例extend了模块,那么里面的方法就是实例方法:
class InstanceThatExtends
end
ClassThatExtends.new.extend(ReusableModule).module_method
也就是说extend的即可以作实例方法,也可以作类方法,而include的只能是作实例方法。这也需要注意另一问题,上述情况的基准是module里面的都是实例方法。如果module里面出现了类方法,那么不应该用include或extends来使用,而应该直接用ModuleName.classMethod的方式来引用。大概解释与java差不多,类方法(静态方法)不被继承使用。