当我们要扩展类的方法时,我们可以采取Mixin的方式将模块中的方法添加到类中,下面会对实现的几种方式进行详细介绍:
module MyMod def method end end class MyClass include MyMod end MyClass.new.method #正确 MyClass.method #错误下面的例子是extend的使用方法。
module MyMod def method end end class MyClass extend MyMod end MyClass.new.method #错误 MyClass.method #正确
module MyMod def self.included(base) puts "included into #{base}" end def self.extended(base) puts "extended into #{base}" end end class MyClass include myMod #included into MyClass extend myMod #extended into MyClass end
module MyMod module InstanceMethods def instance_method end end module ClassMethods def class_method end end def self.included(receiver) receiver.extend ClassMethods receiver.send :include, InstanceMethods end end class MyClass include MyMod end
在该实现方法中,模块MyMod包括两个子模块InstanceMethods和ClassMethods,其中子模块InstanceMethods中的instance_method方法将作为实例方法引入,子模块ClassMethods中的class_method方法将作为类方法引入。在类MyClass中,通过include该模块,可以调用sef.included方法,进而分别引入子模块InstanceMethods和ClassMethods。
module MyMod extend ActiveSupport::Concern def instance_method end module ClassMethods def class_method end end end该代码与第二种方式中代码的作用相同。但不同的是,在使用了ActiveSupport::Concern后,不仅可以省略子模块InstanceMethods,将instance_method方法直接放在模块中,还能省略receiver.extend和receiver.send,自动将类方法和实例方法加载到类中。
module Foo def self.included(base) base.class_eval do def self.method_injected_by_foo ... end end end end module Bar def self.included(base) base.method_injected_by_foo end end class Host include Foo # We need to include this dependency for Bar include Bar # Bar is the module that Host really needs end首先,在上面的代码中,类Host要引用模块Bar,但模块Bar需要调用模块Foo中定义的method_injected_by_foo方法,因此需要先引入模块Foo。但是,为什么不直接在模块Bar中直接引入模块Foo呢?这样可以在类Host中省去对模块Foo的引用,如下面代码所示:
module Foo def self.included(base) base.class_eval do def self.method_injected_by_foo ... end end end end module Bar include Foo def self.included(base) base.method_injected_by_foo end end class Host include Bar end但该代码并不能起作用,这是因为当模块Foo被引入时,其base为模块Bar,而不是类Host,这样我们就无法在类Host中调用模块Foo中的method_injected_by_foo方法。此时,使用ActiveSupport::Concern可以很好地解决此模块依赖的问题,代码如下:
module Foo extend ActiveSupport::Concern included do class_eval do def self.method_injected_by_foo ... end end end end module Bar extend ActiveSupport::Concern include Foo included do self.method_injected_by_foo end end class Host include Bar # works, Bar takes care now of its dependencies end