【Ruby】模块扩展方法介绍

当我们要扩展类的方法时,我们可以采取Mixin的方式将模块中的方法添加到类中,下面会对实现的几种方式进行详细介绍:

1. include和extend


使用include,可以将模块中定义的方法作为实例方法添加到类中;使用extend,可以将模块中定义的方法作为类方法添加到类中。
下面的例子是include的使用方法。
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  #正确
此外,Ruby针对模块还提供了一些Hook方法,这些方法会在模块被Mixin到类中时执行,其中self.included方法会在调用include时执行,self.extended方法会在调用extend时执行。具体示例如下:
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

2. 同时引入实例方法和类方法


使用include和extend可以很好地帮助我们分别引入模块中实例方法和类方法,但如果对于一个模块,我们需要把其中一部分的方法作为实例方法引入,而另一部分的方法作为类方法引入,使用这种处理方式就无法实现。对此,我们利用子模块来进行实现,示例如下:
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。

3. ActiveSupport::Concern


相比于第二种方式,我们可以使用ActiveSupport::Concern来更好地进行处理(需要注意的是:使用前需要require "active_support")。下面的例子是ActiveSupport::Concern的使用方法,其中instance_method表示实例方法,class_method表示类方法。
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,自动将类方法和实例方法加载到类中。
除了可以简化代码以外,ActiveSupport::Concern还有一个更大的作用是:当需要引入多个模块时,可以很好地解决模块相互之间的依赖关系。
下面是 rubydoc上关于ActiveSupport::Concern的讲解。
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

转载请注明出处: http://blog.csdn.net/sunset108/article/details/48956101

你可能感兴趣的:(Module,extend,Ruby,include)