Ruby元编程,定义多个“继承”功能

Oli*_*lay 5 ruby metaprogramming

我希望将以下模块包含在我的课程中:

module InheritanceEnumerator  
  def self.included(klass)  
      klass.instance_eval do  
        instance_variable_set('@subclasses',[])  
        def self.subclasses  
          @subclasses  
        end  
        original_method = self.respond_to?(:inherited) ? self.public_method(:inherited) : nil  
        instance_variable_set('@original_inherited_method', original_method)  
        def self.inherited(subclass)  
          @original_inherited_method.call(subclass) if @original_inherited_method  
          @subclasses<<subclass  
        end  
      end  
   end  
end  
Run Code Online (Sandbox Code Playgroud)

我要实现的目标是希望我的父类具有对直接子代的引用。我还需要其他东西在班级上设置的任何其他以前的“继承”方法,以保持不变。我究竟做错了什么?

hor*_*guy 2

您的代码在这种情况下有效(对我来说):

class C; include InheritanceEnumerator; end
C.subclasses #=> []
class C1 < C; end
class C2 < C; end
C.subclasses #=> [C1, C2]
Run Code Online (Sandbox Code Playgroud)

但在以下情况下会失败:

class C11 < C1; end
C1.subclasses => NoMethodError: undefined method `<<' for nil:NilClass
Run Code Online (Sandbox Code Playgroud)

这是因为您仅@subclasses在包含模块时才进行初始化;但您忘记了 的子类C也可以访问模块方法,但没有显式include访问。

您可以通过执行以下操作来修复此问题:

def self.subclasses
    @subclasses ||= []
    @subclasses
end

def self.inherited(subclass)
    @original_inherited_method.call(subclass) if @original_inherited_method

    @subclasses ||= []
    @subclasses << subclass  
end
Run Code Online (Sandbox Code Playgroud)

编辑:

好的,以后请更全面地说明你的问题是什么,并提供你正在使用的测试代码;因为这是一种挫败感的练习。

以下内容适用于您的代码:

class C
    def self.inherited(s)
        puts "inherited by #{s}!"
    end

    include InheritanceEnumerator
end

class D < C; end #=> "inherited by D!"
C.subclasses #=> [D]
Run Code Online (Sandbox Code Playgroud)

也许它对你不起作用的原因是你在定义方法InheritanceEnumerator 之前inherited就包含了它?