Ruby mixins:扩展和包含
我一直在阅读一些关于Ruby的mixin方法, extend
和include
,我仍然不太确定这种行为。 我理解, extend
会将给定模块的实例方法作为单例方法添加到执行扩展的模块中,并且include
将基本上将模块的内容(方法,常量,变量)附加到执行包含的模块中,有效地定义他们在接收器中。
然而,经过一些修补,试图了解行为将如何表现,我有几个问题。 这是我的测试设置:
module Baz def blorg puts 'blorg' end end module Bar include Baz def blah puts 'blah' end end module Foo extend Bar end class Bacon extend Bar end class Egg include Bar end
正如我所料,模块Bar
获得了Baz
( #blorg
)中定义的实例方法,好像它们由于包含方法而自己定义,而类Bacon
获得了单例方法Bacon::blah
和Bacon::blorg
通过扩展。
Bacon.blah # => blah Bacon.blorg # => blorg
而类Egg
获得Bar
( #blah
和现在#blorg
)中定义的方法作为实例方法。
Egg.new.blah # => blah Egg.new.blorg # => blorg
我得到了所有这些,所以这很好。
但是,我不理解使用#ancestors
和#is_a?
得到的#is_a?
方法。
Bacon.ancestors # => [Bacon, Object, Kernel, BasicObject] Bacon.is_a? Bar # => true Egg.ancestors # => [Egg, Bar, Baz, Object, Kernel, BasicObject] Egg.is_a? Bar # => false
似乎扩展模块会导致#is_a?
查询该模块时返回true
方法,但是没有添加到类的祖先,反之亦然,包含:类的祖先包含包含的模块,但#is_a?
查询时,该方法返回false
。 为什么会这样?
不同之处在于include
会将包含的类添加到包含类的祖先,而extend
会将扩展类添加到扩展类的singleton类的祖先。 唷。 我们先来看看会发生什么:
Bacon.ancestors #=> [Bacon, Object, Kernel, BasicObject] Bacon.singleton_class.ancestors #=> [Bar, Baz, Class, Module, Object, Kernel, BasicObject] Bacon.new.singleton_class.ancestors #=> [Bacon, Object, Kernel, BasicObject] Bacon.is_a? Bar #=> true Bacon.new.is_a? Bar #=> false
而对于Egg
类
Egg.ancestors #=> [Egg, Bar, Baz, Object, Kernel, BasicObject] Egg.singleton_class.ancestors #=> [Class, Module, Object, Kernel, BasicObject] Egg.new.singleton_class.ancestors #=> [Egg, Bar, Baz, Object, Kernel, BasicObject] Egg.is_a? Bar #=> false Egg.new.is_a? Bar #=> true
那么foo.is_a? Klass
是什么foo.is_a? Klass
foo.is_a? Klass
实际上是检查foo.singleton_class.ancestors
是否包含Klass
。 另一件事是,在创建实例时,类的所有祖先都成为实例的单例类的祖先。 因此,对于所有新创建的任何类的实例,这将评估为true:
Egg.ancestors == Egg.new.singleton_class.ancestors
那么,这意味着什么? extend
和include
在不同级别上做同样的事情,我希望下面的例子清楚地说明这两个扩展类的方法本质上是等价的:
module A def foobar puts 'foobar' end end class B extend A end class C class << self include A end end B.singleton_class.ancestors == C.singleton_class.ancestors #=> true
class << self
只是到达单例类的奇怪语法。 所以extend
真的只是在单例类中include
的简写。
Egg.is_a? Egg # => false
包含(有效)更改Egg
类的实例。 虽然它不完全相同,但它与做类似的事情非常相似
class Egg < Bar end
当扩展将添加类方法时,这与做类似的事情非常相似
class Bacon class << self include Bar end end
您可以将其视为包括类的更改实例,其中extend实际上更改了类。