为什么模块的单例方法在混合的下游特征类中不可见?

sai*_*ala 7 ruby inheritance mixins eigenclass

我理解常规方法查找路径即class, superclass/module, all the way up to BasicObject.我认为对于单链版本的链也是如此,但是当您在元链中混合模块时似乎并非如此.我很感激,如果有人能解释为什么在下面的示例中,当我将此模块包含在Vehicle的本征类中时,调用Automobile模块的banner方法而不是单例版本.

module Automobile
  def banner
    "I am a regular method of Automobile"
  end

  class << self
    def banner
      "I am a singleton method of Automobile"
    end
  end
end

class Vehicle 
  def banner
    "I am an instance method of Vehicle"
  end

  class << self
    include Automobile
    def banner
      puts "I am a singleton method of Vehicle"
      super
    end
  end
end

class Car < Vehicle
  def banner
    "I am an instance method of Car"
  end

  class << self
    def banner
      puts "I am a singleton method of Car"
      super
    end
  end
end

puts Car.banner

# I am a singleton method of Car
# I am a singleton method of Vehicle
# I am a regular method of Automobile
Run Code Online (Sandbox Code Playgroud)

Pat*_*ity 8

首先,include不包括您所期望的本征类方法.考虑:

module Foo
  class << self
    def do_something
      puts "Foo's eigenclass method does something"
    end
  end
end

module Bar
  include Foo
end

puts Bar.do_something
# undefined method `do_something' for Bar:Module (NoMethodError)
Run Code Online (Sandbox Code Playgroud)

请注意,这与经典定义的类方法的行为一致:

module Foo
  def self.do_something
    puts "Foo's class method does something"
  end
end

module Bar
  include Foo
end

puts Bar.do_something
# undefined method `do_something' for Bar:Module (NoMethodError)
Run Code Online (Sandbox Code Playgroud)

一个常见的习惯用法是在子模块中定义类方法,然后在包含模块时触发调用extend:

module Foo
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def do_something
      puts "Foo::ClassMethod's instance method does something"
    end
  end
end

module Bar
  include Foo
end

puts Bar.do_something
# Foo::ClassMethod's instance method does something
Run Code Online (Sandbox Code Playgroud)

第二点要注意的是,你实际上是将实例方法包含Automobile到了本征类中Vehicle,因此将实例方法Automobile转化为(本征)类方法Vehicle.

你的Car班级基本上与这一切无关.这里唯一要注意的是,类继承也使类方法可用,而include不是.例:

class Foo
  def self.do_something
    puts "Foo's class method does something"
  end
end

class Bar < Foo
end

puts Bar.do_something
# "Foo's class method does something"
Run Code Online (Sandbox Code Playgroud)