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

让'super'通过包含的模块调用父类

  •  3
  • globewalldesk  · 技术社区  · 7 年前

    我有课 Boy 继承类的 Person ,包括模块 Bipedal .两者都是 人 和 两足动物 具有的版本 #two_legs .

    module Bipedal
      def two_legs(name)
        puts "#{name} has exactly two limbs used for walking."
      end
    end
    
    class Person
      def two_legs(name)
        puts "#{name} has two human legs."
      end
    end
    
    class Boy < Person
      include Bipedal
      attr_accessor :name
      def initialize(name)
        @name = name
      end
    
      def two_legs
        super(@name)
      end
    end
    

    自从 两足动物 模块包含在 男孩 , Bipedal#two_legs 优先于 Person#two_legs . 当我呼唤 super 在一 男孩 实例,模块 两足动物 优先于父类 人 .

    johnny = Boy.new('Johnny')
    johnny.two_legs
    # >> "Johnny has exactly two limbs used for walking."
    

    我想在一个地方使用一个版本,在另一个地方使用另一个版本。 两足动物 还有其他东西,所以我不能评论 include Bipedal . 有什么标准的方法可以让 Boy#two_legs 或 超级的 使用父类版本而不是模块版本,如下所示?

    johnny.two_legs
    # >> "Johnny has two human legs."
    

    我想到了这个:

    Boy.superclass.instance_method(:two_legs).bind(self).call(@name)
    

    它代替了 super(@name) 但比我想象的要复杂。

    同样,问题是,是否有一种标准方法可以强制父类在调用时优先于模块? 超级的 ?

    3 回复  |  直到 7 年前
        1
  •  2
  •   Cary Swoveland    7 年前

    我们可以用这个方法 Method#super_method 两次。作为一种花园风格的Ruby方法,我相信它可以作为 Boy#two_legs 援引 Person#two_legs 而不是 Bipedal#two_legs .

    class Boy < Person
      include Bipedal
      attr_accessor :name
      def initialize(name)
        @name = name
      end
      def two_legs
        method(:two_legs).super_method.super_method.call(@name)
      end
    end
    
    willie = Boy.new('Willie')
    willie.two_legs
    Willie has two human legs.
    

    注意以下内容。

    willie.method(:two_legs).super_method.owner
      #=> Bipedal
    willie.method(:two_legs).super_method.super_method.owner
      #=> Person
    
        2
  •  3
  •   anothermh ScarletMerlin    7 年前

    不,没有标准方法强制呼叫 super 以特定的顺序行走祖先。它们按预先定义的顺序行走。看一看 the documentation on calling methods :

    发送消息时,Ruby会查找与接收者消息名称匹配的方法。方法存储在类和模块中,因此方法查找会执行这些操作,而不是对象本身。

    下面是接收器类或模块的方法查找顺序 R :

    • 预先准备好的模块 R 按相反顺序
    • 对于中的匹配方法 R
    • 包含的模块 R 按相反顺序

    如果 R 是一个具有超类的类,重复使用 R 找到方法之前的超类。

    一旦找到匹配项,方法查找将停止。

    从你上课开始 Boy 包括模块 Bipedal 直接搜索,因为包含的模块在超类之前被搜索,并且由于搜索一旦找到匹配就停止,所以超类 Person 从不检查。

    使用时也是如此 refinements 因为查找总是在检查超类之前命中包含的模块。

    如果你移动 include Bipedal 进入 人 然后它将按您期望的方式工作,因为 男孩 不直接包含模块,因此它最终将在超类中查找定义的方法。

    可能还有其他创造性的方法可以实现这个目标,比如 Boy.superclass... 您给出的示例,但您询问在调用时是否可以更改查找行为 超级的 根据文件,答案是否定的。

        3
  •  2
  •   mu is too short    7 年前

    我觉得你不能改变 super 工作,但你不能” super super “跳过祖先,但可以使用其他祖先。如果你只是包括 Bipedal 对于它的一些方法,如果想跳过其中的一些方法,可以这样做:

    SemiBipedal = Bipedal.dup
    SemiBipedal.remove_method(:two_legs)
    
    class Boy < Person
      include SemiBipedal
      #...
    end
    

    当然 johnny.is_a? Bipedal 不会再是真的了 Boy.ancestors 会有 SemiBipedal 而不是 两足动物 在索引1,但这可能无关紧要。

    推荐文章