5

假设我有一个Flight使用类和实例方法调用的模块。我可以使用 , 或两者将其方法放入一个类includeextend

class Bat < Mammal
  # Add Flight's class methods to Bat.
  extend Flight

  # Add Flight's instance methods to Bat.
  include Flight
  ...
end

include将添加FlightBat.ancestors,但extend不会。

我的问题是,为什么模块与类不同?当我子类化时Mammal,我总是同时获得类和实例方法。但是,当我混入一个模块时,我不能同时获得类和实例方法(除非我使用self.included钩子或类似ActiveSupport::Concern的东西)。

这种差异背后是否存在语言设计问题?

4

3 回答 3

10

两者Module#includeObject#extend都用于将a的实例方法Module添加到Object. 给定模块:

module Flight
    def can_fly?
        true
    end
end

Module#include用于将模块的实例方法添加(或混合)到类或模块的实例方法中:

class Bat < Mammal
    include Flight
end

a = Bat.new()
a.can_fly?        # true

它实际上会影响Object#is_a?方法,因此:

a.is_a? Flight     # true

Module#include是一个私有方法,所以它只能在定义一个类或另一个模块时用函数表示法调用:

class Bat < Mammal
    self.include Flight     # NoMethodError: private method called
end

Object#extend将模块的实例方法作为单例方法添加到调用它的对象中,因此您可以这样做:

b = Mammal.new()
b.extend Flight
b.can_fly?           # true
b.is_a? Flight       # true

c = Mammal.new()
c.can_fly?           # NoMethodError: undefined method

并且只会b有来自的实例方法Flight;其他Mammal物体不会。

在类定义中调用时Object#extend,方法会添加到您正在定义的类的特征类中。这是在类定义中使用这两种方法时的重要区别,因为这些方法是作为类方法添加的:

class Bat < Mammal
    extend Flight
end

Bat.can_fly?     # true

d = Bat.new
d.can_fly?       # NoMethodError: undefined method
于 2012-10-16T11:49:17.900 回答
3

“当我子类化时Mammal,我总是同时获得类和实例方法”

这是因为Bat作为对象的类也继承了Mammal单例类的实例方法。

继承图

将模块包含到类中会改变方法查找链。所以实际上这个类并没有继承任何实例方法。

使用模块扩展类与扩展任何对象相同。该类简单地获取模块的实例方法作为类实例方法(即类对象本身的方法)。

于 2012-10-16T11:40:43.933 回答
3

我想解决你问题的一部分:

include将添加FlightBat.ancestors,但extend不会。

extendinclude不同,因此它显然做了不同的事情……您可以认为extend等于类元类上的include 。

看看下面的例子:

module M
end

class A
  include M
end

# then you will see M within A's ancestors as you know
A.ancestors # => [A, M, Object...]


class B
  # the following is roughly the same as extend M:
  class <<self
    include M
  end
end

# then you will see M within B's metaclass' ancestors
MetaclassOfB = class <<B; self; end
MetaclassOfB.ancestors # => [M, Class, Module...]

因此,由于extend就像元类上的包含,您会看到扩展模块出现在元类的祖先链中......

于 2012-10-16T11:43:14.297 回答