在讲解这关键词之前,先要了解ruby中的Module的概念。ruby是面向对象语言,而类(class)是面向对象语言中一个非常重要的概念,一个类往往映射一个主体(entity). 类具有的属性和行为我们可以通过getter,setter,method的方式写到一个类文件中,但是随着单文件代码的增多,将变得不可维护,这就需要通过其它方式来根据作用的不同,来管理不同模块的代码,所以ruby中引入了module的概念。

声明一个module

module A
end

那module怎么被class引用呢? ruby中有一个概念叫mixin,也就是将module里方法mixin到class里。

include方法

我们使用include实现个简单的例子:

module Nameable
  def hi
    "Hi, I am From Nameable module"
  end
end

class U
  include Nameable
end

u = U.new
u.hi

#=> "Hi, I am From Nameable module"

上面在U类中通过include将Nameable mixin了进来后,U的实例便可以通过类似实例方法的调用方式来调用hi方法。

extend方法

接下来使用extend实现一下:

module Nameable
  def hi
    "Hi, I am From Nameable module"
  end
end

class U
  extend Nameable
end

u = U.new
u.hi
#=>  undefined method `hi' for #<U:0x007fbac31470f0>

U.hi
#=> "Hi, I am From Nameable module"

上面在U类中通过extend将Nameable mixin了进来后,U便可以通过类似类方法的调用方式来调用hi方法。

以上也是include和extend的主要区别,可以理解为include将module中的方法作为实例方法mixin进来,而extend将将module中的方法作为类方法mixin进来。

方法查找

那如果mixin过来的module和类自身有同名的方法,ruby会怎么做呢,再做一个实验.

module Nameable
  def hi
    "Hi, I am From Nameable module"
  end
end

class U
  include Nameable
 
  def hi
    "Hi, I am From U class"
  end
end

u = U.new
u.hi
#=> "Hi, I am From U class"

这表示,U实例再查找hi方法时,会优先在类文件里查找,如果找到hi方法,便不再继续查找,所以没有去查找module里的hi方法,这个在ruby中叫做。

再看一下稍微复杂点的例子:

module Nameable
  def hi
    "Hi, I am From Nameable module"
  end
end

class U
  def hi
    "Hi, I am From U class"
  end
end

class U1 < U
   include Nameable
end

u1 = U1.new
u1.hi
#=> "Hi, I am From Nameable module"

上面代码中U1继承了U类,在U1的实例调用hi方法时,在方法查找链上,会先在U1类文件中查找,接着在minxin进来的Nameable里查找,然后在U类中查找。这里在Nameable里查找成功后结束查找。 我们看一下U1类的祖先链, 便可清晰的看出方法查找链:

U1.ancestors
#=> [U1, Nameable, U, Object, Kernel, BasicObject]

prepend方法

根据上面的例子我们知道,ruby中的方法查找,是先从自身类中查找,然后从mixin或者继承的父类中查找。那如果想优先用mixin module中的方法怎么做呢,这就引出了prepend方法。还是上面最简单的例子:

module Nameable
  def hi
    "Hi, I am From Nameable module"
  end
end

class U
  prepend Nameable
 
  def hi
    "Hi, I am From U class"
  end
end

u = U.new
u.hi
#=> "Hi, I am From Nameable module"

上面prepend将Nameable里的hi方法优先加载进来供U实例查找,而不再调用该类中定义的hi方法。另外prepend和include一样的地方是mixin进来的方法作为实例方法使用。

super关键词

谈到方法查找就不得不涉及ruby中的super关键词。super用来调用父类中的同名方法.

class Animal
  def name
    "Animal"
  end
end
class Cat < Animal
  def name
    "The Cat is an #{super}"
  end
end

Cat.new.name
#=> "The Cat is an Animal"

这样Cat的实例在调用super时,会向上查找父类的同名name方法并调用.

注意: super和super()的要根据父类中方法是否需要传参区分使用。

参考: https://dev.to/d4vsanchez/my-journey-into-ruby-modules-8il

https://www.rubyguides.com/2018/09/ruby-super-keyword/