先列举一个module中变量和常量的例子.
module M1
$globalvar = 0
Constant = 1
local_var = 2
@insvar = 3
@@classvar = 4
def method1(sym)
return eval(sym.id2name)
end
class Myclass1
end
p Constant.object_id
p local_var.object_id
p @insvar.object_id
p @@classvar.object_id
end
# Module中常量和class可以通过::直接引用. 其他的不行
p M1::Constant #=> 1
p M1::Myclass1 #=> M1::Myclass1
# include M1后, 看一下哪些会带入, 哪些不会带入.
include M1
p $globalvar #=> 0 , 全局变量被带入到当前scope
p Constant #=> 1 , 常量被带入到当前scope
p @@classvar #=> 4 , class variable直接带入到当前scope.
p @insvar #=> nil , 说明instance variable 没有被带入当前scope.
p method1(:@insvar) #=> nil, 能调用method1 说明M1的instance method被带入到当前scope. 返回nil说明M1的instance method访问的是当前scope的instance variable. 而不是M1的instance variable.
@insvar = 30
p method1(:@insvar) #=> 30 , 虽然不会带入, 但是module带入的方法访问instance variable时可以访问当前环境的instance variable.
p method1(:@@classvar) #=> 4 , 再次说明include 模块会把模块的class variable带入当前环境.
@@classvar = 40
p method1(:@@classvar) #=> 40 , class variable本身就是共用的, 所以不能说明什么.
obj1 = Myclass1.new
p obj1 # => #<M1::Myclass1:0x13f7bf8> , 说明M1的class被带入到当前scope.
# M1的本地变量不会带入到当前环境,
p local_var # 报错, 说明M1的本地变量不会带入到当前scope
C:/Users/digoal/Desktop/new.rb:22:in `<main>': undefined local variable or method `local_var' for main:Object (NameError)
# 如果使用方法去输出本地变量, 那么这个本地变量必须在方法内定义才会有结果, 否则都是报错, 本地变量生效在什么范围可以翻看以前的文章
def method1(sym)
local_var = 100
return eval(sym.id2name)
end
p method1(:local_var) #=> 100
也就是说, "全局变量, 常量, class variable, instance method, class" 都被带入到当前scope.
local variable, instance variable没有被带入到当前scope.
接下来开始讲解书上提到的一些知识点,
1. Modules and Classes
首先, Module, Class都是Class类的对象.
自定义的module是Module的对象.
自定义的class是Class的对象.
p Module.class
p Class.class
module M1
end
class C1
end
p M1.class
p C1.class
输出
Class
Class
Module
Class
其次, Class的superclass是Module, Module的superclass是Object.
自定义的module没有superclass, 自定义的class的superclass是Object.
如果把上面的注释去掉报错, 因为module无法继承.
因为module没有继承, 不能实例化.
实例化也是报错的
但是class可以继承Module class, 注意不是继承自定义的module.
以下则报错
2. Predefined Modules
p Module.superclass
p Class.superclass
module M1
end
class C1
end
p C1.superclass
# p M1.superclass
输出
Object
Module
Object
如果把上面的注释去掉报错, 因为module无法继承.
p M1.superclass
报错
C:/Users/digoal/Desktop/new.rb:5:in `<main>': undefined method `superclass' for M1:Module (NoMethodError)
因为module没有继承, 不能实例化.
module M1
end
module M2 < M1 # 这里报错
end
报错
C:/Users/digoal/Desktop/new.rb:3: syntax error, unexpected '<'
module M2 < M1
^
实例化也是报错的
module M1
end
m1 = M1.new
报错
C:/Users/digoal/Desktop/new.rb:3:in `<main>': undefined method `new' for M1:Module (NoMethodError)
但是class可以继承Module class, 注意不是继承自定义的module.
class Myclass1 < Module
end
以下则报错
module M1
def method1(var)
return var
end
end
class Myclass1 < M1
end
报错
C:/Users/digoal/Desktop/new.rb:6:in `<main>': wrong argument type Module (expected Class) (TypeError)
2. Predefined Modules
Ruby自带的Module
Comparable, Enumerable, FileTest, GC, Kernel, Math, ObjectSpace, Process, Signal
2.1 Comparable
以前接触过, 它用于Class的objects之间可以用来比较的需求. 定义class时必须把Comparable mixin. 并且需要定义<=>方法.
返回-1,0,1. returning -1, 0, or +1 depending on whether the receiver is less than, equal to, or greater than the other object.
If the other object is not comparable then the
<=>
operator should return nil
Comparable
uses
<=>
to implement the conventional comparison operators (<,
<=,
==,
>=, and>) and the method
between?
示例 :
2.2 Enumerable
class SizeMatters
include Comparable
attr_accessor :str
def <=> (anOther)
str.size <=> anOther.str.size
end
def initialize(str)
@str = str
end
end
s1 = SizeMatters.new("Z")
s2 = SizeMatters.new("YY")
s3 = SizeMatters.new("XXX")
s4 = SizeMatters.new("WWWW")
s5 = SizeMatters.new("VVVVV")
p s1 < s2 #=> true
p s4.between?(s1, s3) #=> false
p s4.between?(s3, s5) #=> true
p [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
输出 :
true
false
true
[#<SizeMatters:0x1efc258 @str="Z">, #<SizeMatters:0x1efc228 @str="YY">, #<SizeMatters:0x1efc1f8 @str="XXX">, #<SizeMatters:0x1efc1c8 @str="WWWW">, #<SizeMatters:0x1efc198 @str="VVVVV">]
2.2 Enumerable
The
Enumerable
mixin provides collection classes with several traversal and searching methods, and with the ability to sort. The class must provide a method
each, which yields successive members of the collection. If
Enumerable# max,
min, or
sort
is used, the objects in the collection must also implement a meaningful
<=>
operator, as these methods rely on an ordering between members of the collection.
If Enumerable# max
, min
, or sort
is used,
the objects in the collection must also implement a meaningful <=>
operator 指的是collection里面的元素必须有<=>方法, 否则无法比较.
例如 ['a','b','c'] 是可比较的. 因为'a' 可以和'b'比.String class 有<=>方法.
p "a" > "b"
p "a".class
p String.method_defined?(:<=>)
输出
false
String
true
2.3 FileTest
implements file test operations similar to those used in
File::Stat. It exists as a standalone module, and its methods are also insinuated into the
File
class. (Note that this is not done by inclusion: the interpreter cheats).
2.4 The GC module provides an interface to Ruby’s mark and sweep garbage collection mechanism. Some of the underlying methods are also available via the ObjectSpace module.
You may obtain information about the operation of the GC through GC::Profiler
2.5 Kernel is a module included by the Object class; it defines Ruby’s “builtin” methods.
Object mixin了Kernel 模块, 因此我们创建的class, 可以使用Kernel模块带的public instance method. 例如puts, print, gets, require等.
2.6 The
Math
module contains module functions for basic trigonometric and transcendental functions. See class
Float
for a list of constants that define Ruby’s floating point accuracy.
例如
2.7 The ObjectSpace module contains a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator.
puts( Math.sqrt(144) ) #=> 12.0
puts( Math::PI ) #=> 3.141592653589793
2.7 The ObjectSpace module contains a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator.
ObjectSpace
also provides support for object finalizers, procs that will be called when a specific object is about to be destroyed by garbage collection.
2.8 The
Process
module is a collection of methods used to manipulate processes.
2.9 Signal is the module for handling signals sent to running processes. The list of available signal names and their interpretation is system dependent.
3. Scope Resolution
class, module, constant都有他们的范围(scope). 使用:: 可以来指定你要访问的class, module, constant的范围.
如 :
module OuterMod
moduleInnerMod
class Class1
end
end
end
OuterMod::InnerMod::Class1
用法是Scope1::Scope2::Scope3 #...etc
::在最前面表示 top-level 的常量
4. Module Funcitons
例子2 :
5. Extending Objects
ACONST = "hello" # This is a top-level constant
module OuterMod
module InnerMod
ACONST=10 # OuterMod::InnerMod::ACONST
class Class1
class Class2
module XYZ
class ABC
ACONST=100 # Deeply nested ACONST
def xyz
puts( ::ACONST ) # <= This refers to top-level ACONST
p ::OuterClass
p ::OuterMod
end
end
end
end
end
end
end
puts(OuterMod::InnerMod::ACONST) #=> 10
puts(OuterMod::InnerMod::Class1::Class2::XYZ::ABC::ACONST) #=> 100
ob = OuterMod::InnerMod::Class1::Class2::XYZ::ABC.new
ob.xyz
输出
10
100
hello
OuterClass
OuterMod
4. Module Funcitons
前面已经讲过,Module singleton method可以通过module_name.method_name来调用. 但是module的instance method需要把module mixin到class后, 通过这个class的object来调用.
现在有一个Module class的private instance method叫做module_function可以通过instance method创建Module singleton method.
module_function(symbol, ...) → self
click to toggle source
Creates module functions for the named methods. These functions may be called with the module as a receiver, and also become available as instance methods to classes that mix in the module. Module functions are copies of the original, and so may be changed independently. The instance-method versions are made private. If used with no arguments, subsequently defined methods become module functions.
也就是说使用module_function带参数时, 将复制原来这个module中定义的instance method, 作为module singleton method. 并且被复制的instance method可以override 而不会影响已经复制的module singleton method.
如果module_function不带参数, 则表示这后面创建的都是module singleton method.
module Mod
def one
"This is one"
end
module_function :one # 带一个参数:one, 表示复制one这个instance method为module singleton method
end
class Cls
include Mod
def call_one
one
end
end
p Mod.one #=> "This is one"
c = Cls.new
p c.call_one #=> "This is one"
module Mod
def one
"This is the new one"
end
end
p Mod.one #=> "This is one"
p c.call_one #=> "This is the new one"
例子2 :
module Mod
def one
"This is one"
end
module_function
def two
return "This is two"
end
def three
return "This is three"
end
end
class Myclass
include Mod
end
ob = Myclass.new
p ob.one #=> "This is one"
p Mod.two #=> "This is two"
p Mod.three #=> "This is three"
p ob.two # 报错, 因为module_function没带参数的话, 后面定义的method就是module singleton method
C:/Users/digoal/Desktop/new.rb:20:in `<main>': private method `two' called for #<Myclass:0x208a940> (NoMethodError)
5. Extending Objects
我们前面讲的Module mixin都是把module mixin到class或module里面. 其实也可以把module的instance method扩展到object. 就好似object的singleton method.
通过Object的public instance method: extend来达到这个目的.
来看个例子.
module Mod
def hello
"Hello from Mod.\n"
end
end
class Klass
def hello
"Hello from Klass.\n"
end
end
k = Klass.new
p k.hello #=> "Hello from Klass.\n"
k.extend(Mod) #=> #<Klass:0x401b3bc8> , Mod的hello方法将覆盖掉k对象的hello方法.
p k.hello #=> "Hello from Mod.\n"
n = Klass.new
p n.hello #=> "Hello from Klass.\n" # 其他对象不会受到影响
结果
"Hello from Klass.\n"
"Hello from Mod.\n"
"Hello from Klass.\n"
如果想给object扩展多个Module. 可以给extend中传入多个参数. 或者分多次调用extend方法. 不过这两种方式有些不同. 如果一次传入多个Module, 如果Module之间有重复的instance method, 那么以参数靠前的method为最终扩展的method.
如果是分多次调用extend的方式, 则以最后一次调用extend时传入的Module中的method为扩展的method.
例如 :
一次传多个模块 :
6. Freezing Objects
module A
def method_y
puts( 'hello from method_y of module A' )
end
end
module B
def method_x
puts( 'hello from method_x of module B' )
end
end
module C
def method_x
puts( 'hello from method_x of module C' )
end
end
class Myclass
def method_y
puts( 'hello from method_y of class Myclass' )
end
end
ob = Myclass.new
ob.extend(A)
ob.method_y #=> hello from method_y of module A
ob.extend(B)
ob.method_y #=> hello from method_y of module A
ob.method_x #=> hello from method_x of module B
ob.extend(C)
ob.method_y #=> hello from method_y of module A
ob.method_x #=> hello from method_x of module C
一次传多个模块 :
ob = Myclass.new
ob.extend(A,B,C)
ob.method_y #=> hello from method_y of module A
ob.method_x #=> hello from method_x of module B , 注意这里是扩展的B模块的method_x方法, 而不是C里面的.
6. Freezing Objects
freeze object的目的是防止未来对对象的修改, 注意这个过程是不可逆的, 也就是不能unfreeze一个freeze的对象.
Object class有一个freeze方法, Module复写了这个方法. 我们接下来讲的是Object class的freeze方法.
被freeze的对象当然也不能给它extend 模块.
也不能给freeze的对象创建singleton method.
判断一个对象是否被freeze, 使用Object class的frozen?方法.
【参考】
freeze → obj click to toggle source
Prevents further modifications to obj. A RuntimeError will be raised if modification is attempted. There is no way to unfreeze a frozen object. See also Object#frozen?.
This method returns self.
a = [ "a", "b", "c" ]
a.freeze
a << "z"
produces:
prog.rb:3:in `<<': can't modify frozen array (RuntimeError)
from prog.rb:3
被freeze的对象当然也不能给它extend 模块.
ob = Myclass.new
ob.freeze
ob.extend(A,B,C)
报错
C:/Users/digoal/Desktop/new.rb:27:in `extend_object': can't modify frozen object (RuntimeError)
from C:/Users/digoal/Desktop/new.rb:27:in `extend'
from C:/Users/digoal/Desktop/new.rb:27:in `<main>'
也不能给freeze的对象创建singleton method.
ob = Myclass.new
ob.freeze
def ob.newmethod
puts("this is a new ob's singleton method")
end
报错
C:/Users/digoal/Desktop/new.rb:27:in `<main>': can't modify frozen Myclass (RuntimeError)
判断一个对象是否被freeze, 使用Object class的frozen?方法.
ob = Myclass.new
p ob.frozen? #=> false
ob.freeze
p ob.frozen? #=> true
【参考】
The Book Of Ruby
Ruby 1.9.3 API