代码之家  ›  专栏  ›  技术社区  ›  starfry

在Ruby中,是否可以将模块向上移动到祖先链?

  •  3
  • starfry  · 技术社区  · 9 年前

    我有一个Ruby类 C 包括一些第三方模块,比如 A B .

    单元 A. 通过以下方式间接包括 C 的类继承链;假设我无法控制在哪里 A. 被指控。现在 C 包括 B 直接,但 B 包括另一个模块 D 其碰巧提供了也由 A. ,就像这样:

    class C < Base
    
      # Base includes A
    
      include B   # includes D
    
      # methods in A overridden by D
    
    end      
    

    祖先链是这样的(其中 ... 表示与此讨论无关的零个或多个其他祖先):

    C ... B ... D ... A 
    

    我想要的功能 A. 接管优先权 D :我想搬家 A. 所以它在前面 D 在祖先链中,如下所示:

    C ... A ... B ... D
    

    我试过简单地包括 A. 再一次,但这没用。有办法做到这一点吗?

    3 回复  |  直到 9 年前
        1
  •  2
  •   sawa    9 年前

    一旦mixin层次结构建立,就不可能更改它。只有包含顺序决定层次结构。你必须包括 A 进入 C (第一次)在您包括 B 进入 C ,或者,如果你提前 A. C 而不是包含它,那么它将优先于 D 即使 B 包含在 C 后来

        2
  •  1
  •   Cary Swoveland    9 年前

    当然可以。我每天做两次,星期天做两次。嗯,有点。。。

    module A
      def hiya(str)
        puts "ho #{str}"
      end
      def if_what?
      end
    end
    
    module D
      def hiya(str)
        puts "hi #{str}"
      end
      def what_if?
      end
    end
    
    module B
      include D
    end
    
    class C
      include A
      include B
    end
    

    如预期:

    C.ancestors
      #=> [C, B, D, A, Object, Kernel, BasicObject] 
    a = C.new
      #=> #<C:0x007fc56324ed40> 
    a.hiya("Lois")
    hi Lois
    

    调用 A 的实例方法,而不是 D 我们可以写:

    (A.instance_methods & D.instance_methods).each { |m| D.send(:remove_method, m) }
    

    让我们看看:

    D.instance_methods
      #=> [:hiya, :what_if?] 
    (A.instance_methods & D.instance_methods).each { |m| D.send(:remove_method, m) }
      #=> [:hiya] 
    D.instance_methods
      #=> [:what_if?] 
    C.instance_methods.include?(:hiya)
      #=> true 
    a.hiya("Lois")
    ho Lois
    
        3
  •  1
  •   Cyno    5 年前

    你几乎可以做到这一点。您不能在层次结构中向上移动一个类/模块,但可以克隆一个模块,并将该匿名模块克隆插入到其他祖先的前面。看看这个:

    module A
      def salutations
        "a"
      end
    end
    
    module D
      def salutations
        "d"
      end
    end
    
    module B
      include D
    end
    
    class Base
      include A
    end
    
    class C < Base
      include B
    end
    
    c = C.new
    puts c.salutations
    
    #=> d
    

    到目前为止,还不错。

    现在:

    module A
      def salutations
        "a"
      end
    end
    
    module D
      def salutations
        "d"
      end
    end
    
    module B
      include D
    end
    
    class Base
      include A
    end
    
    class C < Base
      include B
      def initialize
        aye = A.dup
        self.class.include aye
      end
    end
    
    c = C.new
    puts c.salutations
    
    #=> a
    

    当然,如果您要克隆的模块稍后将被修改,这将不起作用。