简单来说

两个目的:

1.同时增加实例方法和类方法

2.链式增加类方法

首先,定义在module M中的类方法是无法通过include让class C/module N继承到的,因为module M的类方法其实是该module M的单例类的实例方法,而include只是让某个class C/module N的实例查找实例方法时能到被include的module M中查找,调用class C/module N类方法时,完全不会查找到module M的单例类。所以,你只能在M中定义实例方法,然后让C、N去extend M,这样来实现类方法的共享。

(查找一个类的类方法是,会查找到父类的类方法,因为类方法其实是单例类的实例方法,一个类的单例类是其父类的单例类的子类)

而如果还要共享实例方法,那你就需要再定义一个module L,然后让C去include,分两条语句,例如:

class C
  extend ClassMethods
  include InstanceMethod
end

想将ClassMethods与InstanceMethod合在一起(尤其是类方法和实例方法很相关的情况下),可使用ActiveSupport::Concern。extend ActiveSupport::Concern的module M,按照约定,内有module ClassMethods,以使include M的class C能将M::ClassMethods中的方法设为类方法,具体实现方法在append_features中的else的base.extend const_get(:ClassMethods)

此外,还要解决一个问题:一般地,当N和M都内有module ClassMethods,且N.include M时,是无法通过N找到M::ClassMethods的(重名了,只能找到N::ClassMethods ),这就不可能在C.include N时获取M::ClassMethods。为此,需要以append_features中的if的做法来保留对M的指向,于@_dependencies中,然后,在C.include N时,C逐个include @_dependencies中的Concern,之后才extend N::ClassMethods

还有一些要注意的:

1.因M.extend ActiveSupport::Concern,所以ActiveSupport::Concern中的append_features和included不是用def self.xxxx来定义的,而是def xxx,这样才能通过extend使append_features和included成为M的类方法

2.N.include M早于C.include N,才能使C获取M::ClassMethods,因为链式增加类方法是即时extend ClassMethods,而不是什么superclass查找链那种东西

源码

来自gems/activesupport-4.2.1/lib/active_support

module ActiveSupport

  module Concern #通常用于M.extend ActiveSupport::Concern

    #...

    def self.extended(base)
      base.instance_variable_set(:@_dependencies, []) #若M.extend ActiveSupport::Concern,则有base=M,M.@_dependencies
    end

    def append_features(base) #被C.include调用时,即C.include M时,base=C,即M.append_features(C)
      if base.instance_variable_defined?(:@_dependencies) #如果C本身也是concern
        base.instance_variable_get(:@_dependencies) << self #则不作实质的include,而只记录依赖关系
        return false
      else #如果C本身不是concern
        return false if base < self #如果已经include过,则不再次include
        @_dependencies.each { |dep| base.send(:include, dep) } #永远都用base来include本concern曾“include”过的concern,
        super #include本concern
        base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) #永远都用base,即C来extend本concern的ClassMethods
        base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) #在base,即C上class_eval回调block,见下面重写的included
      end
    end

    def included(base = nil, &block) #若M.extend ActiveSupport::Concern,则有M的included(来自Module#included)被重写如下
      if base.nil?
        raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)

        @_included_block = block #以往的included回调方法要写很多base.,但现在改成注册回调block,然后base.class_eval,见上面append_features。实例见ActionController::Flash
      else
        super
      end
    end

    #...

end


图解

如何递归地include和extend: