Modules
Modules 用来把方法、类以及常量等组织在一起。Modules 会给用户带来两个好处:
1、Modules 提供了一个名字空间,防止了名字冲突。
2、Modules 实现了 minxin 功能。
名称空间
当你准备开始越来越大的 Ruby 应用时,就会发现产生了大量可重用的代码-把相关的代码组织成库通过是很具有实用价值的。这样,你会把这些代码进行分隔,存储到不同的文件中,这样可以在不同的 Ruby 程序中使用。
通常,我们用类来组织这些代码,因此你可能把不同的类(或及其相关类)粘贴到不同的文件中。
但是,你也会碰到一些情况,可能有一些代码,不适合用类来组织。
首先,第一步你可以把所的这些东西(重用代码)组织到一个文件中,然后简单在你需要的地方加载这些文件。这是 C 语言常用的方式。但是,这个方法存在一个问题。假如你与一些三角函数的功能,如 sin、cos 等等,你把这些功能放置在一个文件中,如 trig.rb ,以备将来使用。同时,另一个人 (Sally)正在进行另一项工作,她也写了一些她常用的功能,其中就包括 be_good 和 sin,并把它们保存到 moral.rb ,另外,Joe ,正想写一个另外一个程序,需要加载 trig.rb 和 moral.rb 到他的程序中,但是在两个文件中都调用了 sin方法.这将是我们所不期望看到的。
解决问题的办法就是 module 机制。modules 定义了一个名字空间,在这个沙箱中,你定义的方法、常量都能够正常发挥作用,而不用担心和其他方法和常量产生冲突。我们把 Trig 的功能集中到一个 module 中,代码如下:
module Trig
PI = 3.141592654
def Trig.sin(x)
#
end
def Trig.cos(x)
#
end
end
而另一些在 the good and bad moral 中的方法可以这样组织:
module Moral
VERY_BAD = 0
BAD = 1
def Moral.sin(badness)
#
end
end
Modules 的常量命名和类常量相似,首字母大写。方法定义也是一样的。
如果第三方程序需要使用这些 modules ,可以使用 ruby 的 require 语句来简单加载这两个文件并通过限定名来引用:
require 'trig'
require 'moral'
y = Trig.sin(Trig::PI/4)
wrongdong = Moral.sin(Moral::VERY_BAD)
像使用类方法一样,通过在方法前面加上 module 名的前缀来调用 module 方法。通过 ::来引用 module 方法,就像使用类常量一样。
Mixins
Module 另外一个有趣的功能。通过提供叫做 mixin 的东西,来大大减少了对多继承的需要。
在前面章节的例子中,我们定义了 module 方法,通过 module 前缀不引用的方法。如果你直觉感觉这类同于类方法,你下一步会想,“如何包含 module 的实例方法呢?”这个问题问得好,module 不可能包含其实例,因为 module 并不是类。但是,你可以在类定义中 include 一个 module。此时,所有的 module 方法对于类来说如同类方法一样可用。module 声明的方法已经和类融成一体了。实际上,mixed-in 的 module 更加像是一个超类。
module Debug
def who_am_i?
"#{self.class.name}(/##{self.id}):#{self.to_s}"
end
end
class Phonograph
include Debug
#..
end
class EightTrack
include Debug
#..
end
ph = Phonograph.new("West End Blues")
et = EightTrack.new("Surrenalistic Pillow")
ph.who_am_i? ->"Photograpsh(#935520):West End Blues"
et.who_am_i? ->"EightTrack (#935500):Surrealistic Pillow"
通过 include 相应的 Debug module,Photograph 和 EightTrack 类都获得了 who_am_i 的实例方法。
在继续之前,我们可以得出关于include表达式的大量观点。首先,它与文件无关,C 程序员使用 #include 预处理符在编译之前将另一个文件的内容插入到当前文件中。而 ruby 的include 语句仅仅包含一个指向命名 module 的引用。如果 module 在另一个文件中,你必须使用 require 包含它(或者使用 load 来动态加载),然后才能使用 include来包含。第二,ruby 的 include 语句不是仅仅把把 module 的实例方法复制到目标类中,而是生成一个从目标类指向 module 的引用。如果多个类 include 的同一个 module,它们都指向同一个地方,如果你更改了 module 中定义的实例方法,即使你的程序正在运行,所有 include 此 module 的类都会立即生效。
minins 机制给予了我们一种可控制的方式来向类中增加功能。然而,当minin 中的代码和类中的代码开始交互时,其真正强大的能力才得以体现。我们以 ruby 标准的 minin 之 Comparable 为例:
你可能使用 Comparable 的minxin 来增加比较操作(< <= == >= >)以及 between? 方法到你的类中。这些要正常发挥作用, Comparable 假定这些类定义了一个 <=>操作符。因此,如果你准备写一个类,你定义一个<=>方法,然后 include Comparable,你就可以得到6个比较功能。
我们使用我们前面的 Song 类为例,通过其 duration 属性来比较。我们要做有就是include Comparable,然后实现<=>操作符:
class Song
include Comparable
def initialize(name,artist,duration)
@name=name
@artist = artist
@duration=duration
end
def <=>(other)
self.duration<=>other.duration
end
我们通过下面的测试用例来检查结果:
song1 = Song.new("My War", "sinatra", 255)
song2 = Song.new("bicylops", "Fleck", 260)
song1<=> ->-1
song1 < song2 true
迭代和枚举 module -Iterator Enumerable
待续