我理解 mixin 的最佳方式是虚拟类。 Mixin 是注入到类或模块的祖先链中的“虚拟类”。
当我们使用“include”并将模块传递给它时,它会将模块添加到我们要继承的类之前的祖先链中:
class Parent
end
module M
end
class Child < Parent
include M
end
Child.ancestors
=> [Child, M, Parent, Object ...
Ruby 中的每个对象也都有一个单例类。添加到这个单例类的方法可以直接在对象上调用,因此它们充当“类”方法。当我们在对象上使用“扩展”并将对象传递给模块时,我们正在将模块的方法添加到对象的单例类中:
module M
def m
puts 'm'
end
end
class Test
end
Test.extend M
Test.m
我们可以通过 singleton_class 方法访问单例类:
Test.singleton_class.ancestors
=> [#<Class:Test>, M, #<Class:Object>, ...
当模块被混合到类/模块中时,Ruby 为模块提供了一些钩子。 included 是 Ruby 提供的一个钩子方法,只要你在某个模块或类中包含一个模块,就会调用它。就像包含的一样,有一个关联的 extended 挂钩用于扩展。当一个模块被另一个模块或类扩展时,它将被调用。
module M
def self.included(target)
puts "included into #{target}"
end
def self.extended(target)
puts "extended into #{target}"
end
end
class MyClass
include M
end
class MyClass2
extend M
end
这创建了一个开发人员可以使用的有趣模式:
module M
def self.included(target)
target.send(:include, InstanceMethods)
target.extend ClassMethods
target.class_eval do
a_class_method
end
end
module InstanceMethods
def an_instance_method
end
end
module ClassMethods
def a_class_method
puts "a_class_method called"
end
end
end
class MyClass
include M
# a_class_method called
end
如您所见,这个单一模块正在添加实例方法、“类”方法,并直接作用于目标类(在本例中调用 a_class_method())。
ActiveSupport::Concern 封装了这种模式。这是使用 ActiveSupport::Concern 重写的相同模块:
module M
extend ActiveSupport::Concern
included do
a_class_method
end
def an_instance_method
end
module ClassMethods
def a_class_method
puts "a_class_method called"
end
end
end