Ruby 我不知道';我不明白mixin是如何工作的

Ruby 我不知道';我不明白mixin是如何工作的,ruby,Ruby,我有两个模块: module A def name puts "REAL" end end module B def name puts "FAKE" end end 当我将它们包括在我的课堂中时,如下所示: class ABC include A include B end class ABC include B include A end ABC.new.name的输出为: "FAKE" "REAL" 但当我包括如下模块时: cla

我有两个模块:

module A
  def name
    puts "REAL"
  end
end

module B
  def name
    puts "FAKE"
  end
end
当我将它们包括在我的课堂中时,如下所示:

class ABC
  include A
  include B
end
class ABC
  include B
  include A
end
ABC.new.name
的输出为:

"FAKE"
"REAL"
但当我包括如下模块时:

class ABC
  include A
  include B
end
class ABC
  include B
  include A
end
ABC.new.name
的输出为:

"FAKE"
"REAL"

我不明白为什么会这样。有人能帮我理解这一点吗?

当两个模块用相同的名称定义方法时,实际保留在类中的方法是最后包含的方法。

当两个模块用相同的名称定义方法时,实际保留在类中的方法是最后包含的方法。

我不明白这种行为:

irb(main):121:0* module A
irb(main):122:1>   def name
irb(main):123:2>     puts "REAL"
irb(main):124:2>   end
irb(main):125:1> end
=> nil
irb(main):128:0* module B
irb(main):129:1>   def name
irb(main):130:2>     puts "FAKE"
irb(main):131:2>   end
irb(main):132:1> end
=> nil
irb(main):133:0> class ABC
irb(main):134:1>   include A
irb(main):135:1>   include B
irb(main):136:1> end
=> ABC
irb(main):137:0> ABC.new.name
FAKE
=> nil
irb(main):143:0> class XYZ
irb(main):144:1>   include B
irb(main):145:1>   include A
irb(main):146:1> end
=> XYZ
irb(main):147:0> XYZ.new.name
REAL
=> nil

这是有意义的——包含的最后一个模块可以定义该方法。也许您的程序中还有其他问题?

我不明白这种行为:

irb(main):121:0* module A
irb(main):122:1>   def name
irb(main):123:2>     puts "REAL"
irb(main):124:2>   end
irb(main):125:1> end
=> nil
irb(main):128:0* module B
irb(main):129:1>   def name
irb(main):130:2>     puts "FAKE"
irb(main):131:2>   end
irb(main):132:1> end
=> nil
irb(main):133:0> class ABC
irb(main):134:1>   include A
irb(main):135:1>   include B
irb(main):136:1> end
=> ABC
irb(main):137:0> ABC.new.name
FAKE
=> nil
irb(main):143:0> class XYZ
irb(main):144:1>   include B
irb(main):145:1>   include A
irb(main):146:1> end
=> XYZ
irb(main):147:0> XYZ.new.name
REAL
=> nil

这是有意义的——包含的最后一个模块可以定义该方法。也许您的节目中还有其他一些内容?

让我们退一步:什么是混音?Gilad Bracha没有发明mixin(它们是作为Lisp机器Lisp面向对象扩展中的设计模式发明的),但他写了一篇关于它们的开创性论文(他的博士论文),在论文中他定义了什么是mixin,并表明mixin组合包含了所有其他形式的经典继承。根据本文,mixin是由其超类参数化的类。因此,可以将mixin看作一个函数::
类→ 类

这到底是什么意思?因为mixin是由它的超类参数化的,所以它不知道它的超类。当mixin被混合到一个类中时,它将被提供其超类。mixin可以在继承图中多次混合,每次都使用不同的超类。注意:这正是双重到多重继承:在多重继承中,一个类只存在一次,但可能有许多超类。在mixin组合中,mixin存在很多次,但每个实例只有一个超类

这在Ruby中是如何工作的

让我们再次退一步,看看Ruby中的模块在操作上是什么样子的。模块包括:

  • 方法表
  • 固定的桌子
  • 类变量表
班级是什么样子的?类是一个模块,因此它具有上述所有功能,此外,它还具有:

  • 超类指针
当您像这样将模块
M
包含到类
C
中时,会发生什么

class C
  include M
end
首先,这是一种和其他方法一样的方法。这没什么特别的。其默认实现看起来有点像这样:

class Module
  def include(mod)
    mod.append_features(self)
    included(mod)
  end
end
ABC → Object
最后,它调用了hook方法,但我们在这里忽略它。它将实际的mixin组合委托给mixin。(这意味着mixin可以通过覆盖默认的
模块#append_功能
,来决定它要以何种方式进行混合!然而,这很少使用,但例如
ActiveSupport::Concern
将其用于元编程。)

现在我们已经把这个问题推开了。你做什么?好吧,它还是一个和其他方法一样的方法,它可以被覆盖,可以被修补,可以被删除(这不是一个好主意!)。但是,它的默认实现不能用Ruby表示

它的作用是:

  • 创建一个新类,我们称之为
    M'
  • M'
    的方法表指针设置为
    M
    的方法表(常量表和类变量表同上)
  • M′
    的超类指针设置为
    C
    的超类
  • C
    的超类指针设置为
    M'
  • 有效地使
    M′
    成为
    C
    的新超类,使
    C
    的旧超类成为
    M′
    的超类,或者换一种方式,在继承树的
    C
    正上方插入
    M′

    为什么要这样做?因为它使方法查找非常简单:方法查找算法根本不需要了解mixin,它仍然是与在没有mixin的语言中看起来完全相同的算法:

  • 获取接收方的类指针
  • 如果类具有该方法,则执行它,否则,获取超类指针并重复步骤2
  • 如果类指针为空,则调用传递方法名称和参数(除非方法名称
    method\u missing
    ,然后
    raise
    异常)
  • 请注意,该算法的核心是一个非常紧凑的
    ,而在步骤2中则是一个
    循环。紧凑的简单循环很好,毕竟,方法查找是面向对象语言实现中最常执行的操作

    现在,你可能会说,“等等,我问了
    C
    它的
    超类
    ,它不返回
    M′
    ,它返回
    Object
    !”是的,这是真的。但是,它不只是返回超类指针,与方法查找算法不同,它确实知道mixin。或者,更确切地说,它知道YARV的开发人员称之为虚拟类,并且知道在返回超类时跳过虚拟类。虚拟类是YARV内部使用的名称。它指的是包含类(即我上面描述的类,它们是在
    include
    期间创建的)和单例类。反射方法知道如何特别处理它们,例如,忽略它们,知道返回模块
    M
    ,而不是包含类
    M'


    我将在这里暂停,让您在一张纸上运行代码的
    append\u features
    算法和方法查找算法,这样您就可以自己弄清楚为什么会看到所看到的结果


    好的,那么,在你的例子中是怎样的

    我们有一个类
    ABC
    ,它以
    Object
    作为它的