为啥模块的单例方法在混合的下游特征类中不可见?
Posted
技术标签:
【中文标题】为啥模块的单例方法在混合的下游特征类中不可见?【英文标题】:Why a module's singleton method is not visible in downstream eigenclasses where it gets mixed?为什么模块的单例方法在混合的下游特征类中不可见? 【发布时间】:2012-10-27 11:34:50 【问题描述】:我了解常规方法查找路径,即class, superclass/module, all the way up to BasicObject
。我认为对于链的单例版本也是如此,但当您在元链中混合模块时似乎并非如此。如果有人能解释为什么在以下示例中调用 Automobile
模块的 banner
方法而不是它的单例版本,当我将此模块包含在 Vehicle 的 eigenclass 中时,我将不胜感激。
module Automobile
def banner
"I am a regular method of Automobile"
end
class << self
def banner
"I am a class 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 class 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 class method of Car"
super
end
end
end
puts Car.banner
# I am a class method of Car
# I am a class method of Vehicle
# I am a regular method of Automobile
【问题讨论】:
【参考方案1】:首先,类是一个对象,就像其他对象一样,它也有自己的超类;
其次,Eigenclass 本身是一个普通的类,只有匿名且有点不可见;
第三,派生类的eigenclass的超类是基类的eigenclass;
第四,include
包含被包含模块的实例方法(不是单例方法),使其成为receiver类对象的实例方法。
您的示例中有两个并行的继承链
汽车
汽车的特征类
对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
在 eigenclass 继承链中。但遗憾的是,ancestor
方法并没有返回不可见的特征类,但它们实际上是在第二条链中。
【讨论】:
我喜欢你在Object
上的eigenclass
定义。您应该将ancestor
更改为ancestors
。
像上面一样给Object添加一个eigenclass方法对检查很有帮助。很惊讶这并没有在默认情况下进入 Ruby【参考方案2】:
首先,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"
【讨论】:
首先感谢您的回复。我知道当我包含一个模块时,它的特征类方法被忽略,只有它的实例方法被暴露给类。我真正想了解的是这种行为背后的基本原理? 这只是一致的行为。但老实说,我不知道为什么你不能include
类方法。
也许我们从不同的角度来看它,但是当你扩展一个类时,它的实例以及 eigenclass 方法都会暴露给子类(在这个例子中,Car 可以访问 Vehicle.banner
作为以及Vehicle.new.banner
)。因此,从 Vehicle 中保留汽车的单例 banner
方法对我来说似乎不一致。
可以,但是继承不一样! include
根据定义只包括实例方法。类继承两者兼而有之。请记住,模块只不过是一种将其他代码组合在一起的机制。
搜索我在 Matz 单词中找到的档案 - Mix-in is used for several purposes and some of them can be hindered by inheriting class methods, for example, I don't want to spill internal methods when including the Math module. I am not against for some kind of inclusion that inherits class methods as well, but it should be separated from the current #include behavior.
以上是关于为啥模块的单例方法在混合的下游特征类中不可见?的主要内容,如果未能解决你的问题,请参考以下文章
gcc 编译错误:模板类表中嵌套类 A 的成员在嵌套朋友类中不可见。为啥?