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

时间:2012-11-07 11:55:49

标签: ruby inheritance mixins eigenclass

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

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

2 个答案:

答案 0 :(得分: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)

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

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)

一个常见的习惯用法是在子模块中定义类方法,然后在包含模块时触发对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

第二点需要注意的是,您确实将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"

答案 1 :(得分:2)

首先,类是一个对象,就像其他对象一样,它也有自己的超类; 第二,Eigenclass本身是一个普通的类,只有匿名和sorta隐形; 第三,派生类的特征类的超类是基类的本征类; 第四,include包括所包含模块的实例方法(不是单例方法),使它们成为接收器类对象的实例方法。

您的示例中有两个并行的继承链

Car&lt;车辆&lt; ...

汽车的本征类&lt;车辆的本征类&lt;汽车&lt; ...

在irb上进行以下测试:

class Object
  def eigenclass
    class << self
      self
    end
  end
end

Car.ancestors # => [Car, Vehicle, Object, Kernel, BasicObject]
Car.eigenclass.ancestors # => [Automobile, Class, Module, Object, Kernel, BasicObject]
Vehicle.eigenclass.ancestors # => [Automobile, Class, Module, Object, Kernel, BasicObject]
Car.eigenclass.superclass.equal? Vehicle.eigenclass # => true

您看,Automobile位于本征类继承链中。但令人遗憾的是,ancestor方法并没有返回不可见的本征类,但它们实际上是在第二个链中。