Ruby-Module初探

原创 2015年11月18日 21:01:32

Ruby-Module初探

module是什么?

Ruby里有一种叫module的东西,他和我们比较熟知的Class有很多类似之处,同样是组合一堆常量、变量和方法的单元,但是module不能像类一样被实例化。
Class被定义为Module的子类,但Class不只是module的一个子集,Class的某些独有的特性是module不具备的,比如继承或派生的概念。

扩展阅读:

那Class怎么成为Module子类的?

module的两大用途

1. 命名空间(Namespace)

我们可以使用module去构建某一模组的命名空间,这一模组可能包括一些方法或属性,甚至包括一些类,class其实也构建了命名空间,但是module比class显得更简洁、可控,我们可以打开module去为其添加新的属性、方法或类,但是这种更改是静态或是全局的。
比如,如果我们要写一套用于BASE64编解码的功能模块,因为BASE64是一套既定的规则(算法),我们没必要去定义一个类,然后每次要做BASE64编解码的时候,在内存空间中去一个新建一个对象来使用类中的方法;或者到这里我们想到了在类中定义静态方法来避免新建动态对象,的确,在诸如Java这样的语言中,我们习惯这样做,但是Ruby能为我们提供更纯粹的方式,那就是定义一个module:

module Base64
  def self.encode(plain_text)
    # do_encoding
  end

  def self.decode(base64_str)
    # do_decoding
  end
end

编解码是一个很泛的概念,比如还有URL编码、图片编码、视频编码等等,现在我们定义这个模块,就我们实现的编解码方法划定了一个特定命名空间;有了这个模块,我们就可以从以下方面来灵活应用:
一方面,在想做编解码的地方,可以用模块加半角圆点前缀的方式(如Base64.encode(pain_text))直接调用这个模块所提供的方法;
请注意到,我们在模块方法的定义中都加了self.前缀,如果我们想单独使用这个模块而不是作为其它模块或类定义的一部分,这是必要的,因为module的使用实际上和class相差无几,而module又不能实例化,我们需要将其定义为这个module对象(module也是对象哦!那self就是当前正在定义的这个module)的单例方法(静态方法);当然,如果这个模块里的所有方法都想要有被单独使用的能力,那我们可以通过extend的方式来简化代码的编写,就像下面这样:

module Base64
    extend self

    def encode(plain_text)
      # do_encoding
    end

    def decode(base64_str)
      # do_decoding
    end
end

另一方面,如果我们想为一个正在定义中的模块或类增加BASE64的处理能力,那我们就可以通过module的第二个强大的功能来完成这个需求,这个功能就是下面我们提到的混入。

2. 混入(Mixins)

前文提到module没有继承和派生的概念,我们不能用继承关键字去继承一个module,如果要在一个特定Class (ConcreteClass)中去使用另一个已定义的module (ConcreteModule),我们需要使用include的方式将ConcreteModule里定义的东西组装到我们当前所要设计的单元中来,然后ConcreteModule里定义的东西就好像我们直接在ConcreteClass里定义的一样。

# demo_include_module.rb
module ConcreteModule
  ConstA = 'constant A defined in ConcreteModule'

  def method_a
    puts 'method_a defined in module ConcreteModule'
  end
end

class ConcreteClass
  include ConcreteModule

  def method_b
    puts 'method_b defined in class ConcreteClass'
  end
end

puts ConcreteClass::ConstA
ConcreteClass.new.method_a
ConcreteClass.new.method_b

# demo_include_module.rb
module ConcreteModule
  ConstA = 'constant A defined in ConcreteModule'

  def method_a
    puts 'method_a defined in module ConcreteModule'
  end
end

class ConcreteClass
  include ConcreteModule

  def method_b
    puts 'method_b defined in class ConcreteClass'
  end
end

puts ConcreteClass::ConstA
ConcreteClass.method_a
ConcreteClass.new.method_a

执行这段Ruby代码:

> ruby demo_include_module.rb

将会得到如下输出:

constant A defined in ConcreteModule
method_a defined in module ConcreteModule
method_b defined in class ConcreteClass

如果我们想让ConcreteModule中的方法变成ConcreteClass的类方法,只需将类名后面的include换为extend来引入ConcreteModule即可。

# demo_extend_module.rb
module ConcreteModule
  ConstA = 'constant A defined in ConcreteModule'

  def method_a
    puts 'method_a defined in module ConcreteModule'
  end
end

class ConcreteClass
  extend ConcreteModule

  def method_b
    puts 'method_b defined in class ConcreteClass'
  end
end

puts ConcreteModule::ConstA
ConcreteClass.method_a
ConcreteClass.new.method_a

然后执行代码:

> ruby demo_extend_module.rb
constant A defined in ConcreteModule
/xx/yy/demo_extend_module.rb:20:in `<top (required)>': undefined method `method_a' for #<ConcreteClass:0x2e663c8> (NoMethodError)
method_a defined in module ConcreteModule
    from -e:1:in `load'
    from -e:1:in `<main>'

Process finished with exit code 1

可以看到,extend引入的模块里的方法变成了目标类(对象)的单例方法,而且Ruby不允许我们像调用实例方法那样去在一个new出来的对象上调用单例方法。

扩展问题:

  1. 如果ConcreteModule本来就extend了self,那我们再通过include的方式引入ConcreteModule会有什么效果?
  2. ConcreteModule中定义的常量会以怎样的方式进入ConcreteClass?

上文提及的include和extend就是module的混入,通过混入,我们可以引入预先定义的各种模块,组成更强大的模块或类,并且能够方便地从模块中获得静态方法和实例方法。

钩子方法

我们在定义一个module时,还可以为其定义钩子方法,钩子方法名以动词的过去分词形式出现,当模块被以不同方式(include, extend)加入其它模块或类时,对应的*ed(过去分词)回调方法将得到调用,有了钩子方法,我们可以去监控定义了钩子方法的模块被其它定义所引用的情况:

# demo_module_included_hook.rb
module ConcreteModuleBase
  def self.included(other)
    puts "#{other} included #{self}"
  end
end

class ConcreteClass
  include ConcreteModuleBase
end

# cc = ConcreteClass.new

执行包含以上代码的.rb文件:

>ruby demo_module_included_hook.rb
ConcreteClass included ConcreteModuleBase

通过输出结果我们可以看到,程序侦测到了ConcreteModuleBase被引用的情况,这里已经体现了Ruby动态编程的些许魅力(关于Ruby动态编程,这里暂不讨论)!
除了included, extended, 我们还可以定义inherited, method_added, singleton_method_added等钩子方法来侦测不同行为的发生,真是很nice的一个特性。

小结

以上讨论了module的基本概念,两大用途,以及Ruby的钩子机制怎样去监控一个特定module是怎样被引用或是被扩展的,这些远不是module的全部内涵,比如模块函数(module_function)、作用域(scope),还有module的加载机制,均未涉及。
module的更多机制、特性或是用法技巧,有待更多的讨论和发掘。本次讨论,我们以最后一个扩展问题来结束:

扩展问题:

Ruby中没有专门定义接口(interface),为什么?

相关文章推荐

给定A, B两个整数,不使用除法和取模运算,求A/B的商和余数

给定A, B两个整数,不使用除法和取模运算,求A/B的商和余数。 1.   最基本的算法是,从小到大遍历: for (i = 2 to A -1)          if (i * B > A)...

利用K-means聚类算法根据经纬度坐标对中国省市进行聚类

K-means聚类算法是一种非层次聚类算法,在最小误差的基础上将数据划分了特定的类,类间利用距离作为相似度指标,两个向量之间的距离越小,其相似度就越高。程序读取全国省市经纬度坐标,然后根据经纬度坐标进...

Radon变换理论介绍与matlab实现--经验交流

本人最近在研究Radon变换,在查阅了各种资料之后在此写下个人的理解,希望与各位牛牛进行交流共同进步,也使得理解更加深刻些。 Radon变换的本质是将原来的函数做了一个空间转换,即,将原来的XY平...

CT图像重建技术

由于csdn贴图不方便,并且不能上传附件,我把原文上传到了资源空间CT图像重建技术 1.引言 计算机层析成像(Computed Tomography,CT)是通过对物体进行不同角度的射线投影测量而...

Matlab绘图-很详细,很全面

Matlab绘图强大的绘图功能是Matlab的特点之一,Matlab提供了一系列的绘图函数,用户不需要过多的考虑绘图的细节,只需要给出一些基本参数就能得到所需图形,这类函数称为高层绘图函数。此外,Ma...

linux查找目录下的所有文件中是否含有某个字符串

查找目录下的所有文件中是否含有某个字符串  find .|xargs grep -ri "IBM"  查找目录下的所有文件中是否含有某个字符串,并且只打印出文件名  find .|xargs g...

Radon变换入门matlab CT原理

http://hi.baidu.com/hi9394/blog/item/0d492b8bfd714700c8fc7aa9.html 简介 图像投影,就是说将图像在某一方向上做线性积分(或理解为累...

Excel2016右键新建工作表,打开时提示“因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配。”的解决办法

上午新建excel工作表时,发现新建完之后居然打不开 提示: 尼玛这坑爹的,难道我的Excel坏了?? 排查问题之后发现 只有新建“Microsoft Excel 工作表”时...

js弹出框、对话框、提示框、弹窗总结

js弹出框、对话框、提示框、弹窗总结 一、JS的三种最常见的对话框 [javascript] view plaincopy //=...

Intel系列处理器的三种工作模式

Intel系列处理器的三种工作模式 微机中常用的Intel系列微处理器的主要发展过程是:8080,8086/8088,80186, 80286,80386,80486,Pentium,Pen...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Ruby-Module初探
举报原因:
原因补充:

(最多只允许输入30个字)