【问题标题】:Wrap inherited method in aliases将继承的方法包装在别名中
【发布时间】:2014-01-13 03:35:25
【问题描述】:

我想通过继承基类来提供一致的接口。但是我想用额外的功能包装那个基类上的某些方法。

假设每个子类都有一个 eat 方法:

class ClassyAnimal

  def self.inherited(base)
    base.class_eval do
      alias :eat_without_napkin :eat
      alias :eat :eat_with_napkin 
    end
  end

  def eat_with_napkin
    begin
      eat_without_napkin
    rescue FoodOutOfMouthError
      puts 'phew!'
    end
  end

  def eat
    raise 'Please implement the #eat method.'
  end
end

class ClassyWalrus < ClassyAnimal

  def eat
    puts 'Eating Cronuts.'
    raise FoodOutOfMouthError.new('Damn these tusks!')
    puts 'Finishing my meal.'
  end
end

class FoodOutOfMouthError < StandardError; end

现在我希望ClassyWalrus.new.eat 会输出以下内容:

Eating Cronuts.
phew!

不幸的是,我们得到以下结果:

Eating Cronuts.
FoodOutOfMouthError: Damn these tusks!

在继承时,因此在alias 时,#eat 被定义为 ClassyAnimal 内部的那个,而不是 ClassyWalrus 内部的那个。别名 #eat 然后被子类覆盖,包装丢失。

可以做什么?

【问题讨论】:

    标签: ruby inheritance methods


    【解决方案1】:

    尝试在代码中添加以下内容。

    在课堂上ClassyAnimal:

      def eat_with_napkin
        begin
          yield
        rescue FoodOutOfMouthError
          puts 'phew!'
        end
      end
    

    在课堂上ClassyWalrus:

      def eat
        puts 'Eating Cronuts.'
        superclass.eat do
          raise FoodOutOfMouthError.new('Damn these tusks!')
        end
        puts 'Finishing my meal.'
      end
    

    【讨论】:

    • 这可行,但我想避免要求每个实现显式调用 super 或手动包装调用。 #eat 应该是需要做的事情,我想用额外的功能(日志记录、错误处理、检测)来包装它们。他们不应该不小心忘记调用该方法。
    • @Chris,我明白你的意思,但也可以忘记在子类 eat 方法中引发异常。这实际上不是在基类中调用方法的一种方式吗?如果您确实在基类中调用方法,那么使用 inherited 挂钩和别名有什么意义?
    • 因为 self.inherited(base) 仅在 ClassyWalrus &lt; ClassyAnimal 的类继承上被调用,您不能在定义它们之前给函数起别名。我会认为
    • @CarySwoveland 不幸的是,异常并不总是预期的(明确提出),而且要包装的不仅仅是 begin/rescue 子句:( 另一种选择是让孩子实现 #eat 但被称为ClassyWalrus.eat
    • @majioa 你是对的。我可以让它与别名一起使用的唯一方法是将它一直放在 ClassyWalrus 定义的底部。它实际上给 ClassyAnimal#eat 起别名,然后在子类中覆盖它
    【解决方案2】:

    我想到的一种可能性,我相信 ActionMailer 是如何做到这一点的,是将子级的方法定义为实例变量,但在类本身上调用它。

    这不是我想要的,所以我不会接受这个答案,只是为了记录它。

    class ClassyAnimal
      def self.eat
        if instance_methods.include? :eat
          begin
            self.new.eat
          rescue FoodOutOfMouthError
            puts 'phew!'
          end
        end
      end
    end
    
    class ClassyWalrus < ClassyAnimal
    
      def eat
        puts 'Eating Cronuts.'
        raise FoodOutOfMouthError.new('Damn these tusks!')
        puts 'Finishing my meal.'
      end
    end
    
    class FoodOutOfMouthError < StandardError; end
    

    这将被称为:

    ClassyWalrus.eat
    # outputs => Eating Cronuts.
    # outputs => phew!
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 2010-11-20
      • 2017-04-14
      • 2017-03-30
      • 2019-03-15
      相关资源
      最近更新 更多