如何使用Ruby mixins作为类的补丁

时间:2012-04-13 06:39:32

标签: ruby metaprogramming mixins

我开始研究Ruby,因为我正在寻找一种更加动态的Java替代品。 我喜欢在定义之后如何修改Ruby中的类,例如:

class A
  def print
    "A"
  end
end

class B < A
  def print
    super + "B"
  end
end

class A
  alias_method :print_orig, :print
  def print
    print_orig + "+"
  end
end

puts B.new.print # A+B

现在我尝试用mixins做同样的事情:

class A
  def print
    "A"
  end
end

class B < A
  def print
    super + "B"
  end
end

module Plus
  alias_method :print_orig, :print
  def print
    print_orig + "+"
  end
end

A.extend(Plus) # variant 1
B.extend(Plus) # variant 2
class A # variant 3
  include Plus
end
class B # variant 4
  include Plus
end
puts B.new.print

然而,没有一种变体产生预期的结果。顺便说一下,预期结果如下:我希望能够用mixin“修补”A类,以便修改它的行为。我想使用mixins,因为我想用相同的行为“修补”几个类。

有可能做我想要的吗?如果是,怎么样?

2 个答案:

答案 0 :(得分:5)

您的模块代码不起作用,因为它在错误的上下文中执行。您需要在A的上下文中执行它,但它会在Plus的上下文中进行评估。这意味着,您需要将selfPlus更改为A

观察:

class A
  def print
    "A"
  end
end

class B < A
  def print
    super + "B"
  end
end

module Plus
  self # => Plus
  def self.included base
    self # => Plus
    base # => A
    base.class_eval do
      self # => A
      alias_method :print_orig, :print
      def print
        print_orig + "+"
      end
    end
  end
end

A.send :include, Plus
B.new.print # => "A+B"

答案 1 :(得分:1)

你不能以这种方式真正使用Mixins。你在课堂和混音之间产生了冲突。 Mixins implicitly resolve the conflict by linearization.底线是:如果发生冲突,该类的方法优于mixin。要解决此问题,您可以使用Sergio' Tulentsev's approach and have the mixin change its base class aggressively

或者,您可以反射性地添加方法。考虑这个我从Mark's blog偷来的例子。

class Talker

  [:hello, :good_bye].each do |arg|
    method_name = ("say_" + arg.to_s).to_sym
    send :define_method, method_name do
      puts arg
    end
  end

end


t = Talker.new
t.say_hello
t.say_good_bye