Ruby on Rails 模组(Module)

如果我有一个小猫类别,我想要这个小猫类别有飞行功能,你会怎麽做?

直接写一个有飞行功能的小鸟类别,然後再叫小猫类别去继承它?
直接把飞行功能写在小猫类别里?
「继承」的概念是:

我只要让小猫类别去继承小鸟类别就好啦,反正小鸟会飞,所以继承之後的小猫就会飞了!

但第 1 种做法的设计有点怪怪的,好好的猫不当,为什麽要去当鸟?为了想要有飞行功能就去当别人家的小孩。第 2 种做法看来似乎可行,但如果之後又有「我希望我的这个小狗类别也会飞!」的需求,那这样又得在小狗类别里写一段飞行功能,飞行功能的程序码没办法共用。

这时候,模组就可以派上用场了。

飞行模组
在 Ruby 定义模组,使用的是 module 这个关键字:

module Flyable
  def fly
    puts "I can fly!"
  end
end

写起来的手感跟类别一样,连模组名字的规定也跟类别一样,必须是常数(也就是大字英文字母开头)。模组定义好了之後,如果要把它拿来用,只要用 include 这个方法:

class Cat
  include Flyable
end

kitty = Cat.new
kitty.fly        # => I can fly!

把这个飞行模组挂上去,然後小猫就会飞了!之後如果小狗类别也想要有飞行功能的话,只要这样一行:

class Dog
  include Flyable
end

小狗也会飞了。

类别跟模组写起来好像?
如果你注意到,在写类别或模组的时候,除了一个是用 class,另一个是用 module,其它几乎没什麽差别。事实上,在 Ruby 里,「类别」的上层类别就是「模组」,开 IRB 实验一下:

$ irb
>> Class.superclass
=> Module
既然类别跟模组之间是继承关系,让我们来看看这两个类别之间的差别:

$ irb
>> Class.instance_methods - Module.instance_methods
=> [:new, :allocate, :superclass]

可以发现身为「後代」的 Class 类别,比 Module 类别多了 3 个方法,就是因为 Module 类别少了这 3 个方法,所以它跟 Class 最大的差别,就是:

模组没办法 new 一个新的实体出来。
模组没办法继承别的模组。
除此之外,模组跟类别在本质上没什麽太大的差别。

要用继承还是要用模组?
如果你发现你要做的这个功能,它可能在很多不同体系的类别里都会用得到,那你可以考虑把功能写在模组里,需要的时候再 include 进来即可。但如果你还是不知道到底类别跟模组有什麽差别,我再举二个例子。

不知道大家有没看过火影忍者这部漫画,漫画里的主人公之一,宇智波佐助,因为他们家族血统的关系,他写轮眼这个功能是天生就有的,你可以想像他这个功能算是从他的家族「继承」来的。而佐助的老师,旗木卡卡西,他虽然也有写轮眼功能,但他的写轮眼并非继承来的,事实上是他在年轻时候 include 了某位朋友的写轮眼模组,所以才有这个效果。

另一个例子,海贼王漫画里,主角鲁夫本来是普通人,但在偶然的机会下,他 include 了橡胶果实之後,他就有了橡胶人的能力了,并不是因为他老爸是橡胶人所以他才是橡胶人。

参考资料

[为你自己学Ruby on Rails]https://railsbook.tw/chapters/08-ruby-basic-4.html


<<:  Flutter体验 Day 4-Dart CheatSheet (2)

>>:  Day 12 ( 中级 ) 猫咪跑步 ( 超长背景 )

Day 11 利用 docker 安装 nginx 并配置 https

现今的浏览器如 Firefox, Google Chrome 多以将仅 http 的网站和连结标注为...

Day 29. F2E-完善过渡动画

昨天後来在看效果时,有发现过渡动画的元素已经完全超出卡片组件的范围了,这个不是我们想要的效果 理想...

Day-16 : AJAX勾系虾米?

不知不觉来到第16天, 今天我们要来讨论AJAX, 暂时先跳来这边, 来讲讲我经过这一个月专案, 吸...

Day 19 | FPS灭火AR游戏开发Part4 - 喷水、灭火

今天要来实作灭火的行为,包含了水柱的喷射以及火焰的熄灭,那就继续看下去吧! 目录 为火焰添加碰撞器 ...

[Day13]PHP 匿名函式及箭头函式

PHP函数 匿名函数 匿名函数(Anonymous functions),也称作闭包函数(closu...