在Ruby中,围绕子方法执行块的最佳方法是什么?

时间:2018-03-29 19:03:46

标签: ruby inheritance

我有一个父类:

1

和一些子课程 - 目前有五个,还会有更多:

class Base
  def my_method
    block_method do
      # EXECUTE WHATEVER'S IN THE CHILD VERSION OF my_method
      # HOW TO DO?
    end
  end

  def block_method
    original_foo = 'foo'
    foo = 'CUSTOM FOO'

    yield

    foo = original_foo
  end
end

我想在每个class ChildA < Base def my_method puts 'apple' puts 'aardvark' end end class ChildE < Base def my_method puts 'eel' puts 'elephant' end end Child的持续时间内更改变量引用的内容。

我的想法是通过将每个#my_method的{​​{1}}的功能包装在一个块中来实现。但我宁愿在父类中执行此操作,也不必将每个子类的Child包装在完全相同的块中。

关于我如何做到这一点的任何见解?

如果与#my_method存在某种相反的情况,我想这将是实现我想要做的事情的一种方式。

2 个答案:

答案 0 :(得分:3)

你可以使用模块和prepend来表达你所谓的“与超级相反”的想法:

module MethodThing
  # added to remove binding from class since 
  # #my_method relies on the existence of #foo and #foo=
  def self.prepended(base)
    base.attr_reader(:foo) unless base.method_defined?(:foo)
    base.attr_writer(:foo) unless base.method_defined?(:foo=)
  end
  def my_method
    puts "Before: #{foo}"
    original_foo = foo
    self.foo= 'CUSTOM FOO'
    begin
      super
    rescue NoMethodError
      warn "(skipped) #{self.class}##{__method__} not defined"
    end
    self.foo = original_foo
    puts "After: #{foo}"
  end
end

在继承上添加模块

class Base 
  def self.inherited(child)
    child.prepend(MethodThing)
  end

  attr_accessor :foo 
  def initialize
    @foo = 12
  end
end


class ChildA < Base
  def my_method
      puts 'apple'
      puts "During: #{foo}"
      puts 'aardvark'
  end
end

class ChildE < Base
end

输出:

ChildA.new.my_method
# Before: 12
# apple
# During: CUSTOM FOO
# aardvark
# After: 12
ChildE.new.my_method
# Before: 12
# (skipped) ChildE#my_method not defined
# After: 12

还有其他奇怪的方法可以通过继承实现这一点,例如

class Base
 class << self
   attr_accessor :delegate_my_method
   def method_added(method_name)
     if method_name.to_s == "my_method" && self.name != "Base"
       warn "#{self.name}#my_method has been overwritten use delegate_my_method instead" 
     end
   end
 end
 attr_accessor :foo       

 def my_method
   puts "Before: #{foo}"
    original_foo = foo
    self.foo= 'CUSTOM FOO'
    begin
      method(self.class.delegate_my_method.to_s).()
    rescue NameError, TypeError
      warn "(skipped) #{self.class} method delegation not defined
    end
    self.foo = original_foo
    puts "After: #{foo}"
 end
end

class ChildA < Base
  self.delegate_my_method = :delegation_method

  def delegation_method
    puts 'apple'
    puts "During: #{foo}"
    puts 'aardvark'
  end
end 

我可能继续用陌生人和陌生人的方式来解决这个问题,但我认为这些会让你到达你需要去的地方。

答案 1 :(得分:2)

一种选择是定义父类可以调用的私有或nodoc方法,并在每个子节点中定义。

class Parent
  def my_method
    block_method do
      my_method_behavior
    end
  end

  def block_method
    original_foo = @foo
    @foo = 'CUSTOM FOO'

    yield

    @foo = original_foo
  end
end

class Child1 < Parent
  def my_method_behavior
    puts @foo
  end
end

class Child2 < Parent
  def my_method_behavior
    puts @foo
  end
end