通过在另一个模块中混合来覆盖模块混合行为

时间:2017-01-25 23:31:12

标签: ruby

我希望能够来回切换实例如何响应消息。我希望通过混合模块,然后在另一个模块中混合来覆盖该行为来做到这一点。

示例:

module Dog
  def speak
    puts "woof woof"
  end
end

module Cat
  def speak
    puts "meow"
  end
end

class Animal
end

现在我想来回切换Animal的实例如何响应speak消息:

animal = Animal.new

animal.extend(Cat)
animal.speak

animal.extend(Dog)
animal.speak

animal.extend(Cat)
animal.speak

animal.extend(Dog)
animal.speak

animal.extend(Cat)
animal.speak

我希望此代码输出以下内容:

meow
woof woof
meow
woof woof
meow

相反,它输出如下:

meow
woof woof
woof woof
woof woof
woof woof

有关如何使其按预期工作的任何提示?

2 个答案:

答案 0 :(得分:4)

我在Adapter Pattern in ruby: Accessing Your Instance Variables

的另一个问题上调整了答案
module Dog
  def speak
    puts "woof woof"
  end
end

module Cat
  def speak
    puts "meow"
  end
end

module Module_manager
  attr_accessor :name
  def extend mod
    @ancestors ||= {}
    return if @ancestors[mod]
    remove @name if @name
    @name = mod
    mod_clone = mod.clone
    @ancestors[mod] = mod_clone
    super mod_clone
  end

  def remove mod
    mod_clone = @ancestors[mod]
    mod_clone.instance_methods.each {|m| mod_clone.module_eval {remove_method m } }
    @ancestors[mod] = nil
  end
end

class Animal
  include Module_manager
end

animal = Animal.new

animal.extend(Cat)
animal.speak # meow

animal.extend(Dog)
animal.speak # woof woof

animal.extend(Cat)
animal.speak # meow

animal.extend(Dog)
animal.speak # woof woof

animal.extend(Cat)
animal.speak # meow

答案 1 :(得分:2)

我不确定以下内容是否能回答您的问题,但这是实现相同行为的更简单方法。

class Animal
  include Dog
  alias :dog_speak :speak
  include Cat
  alias :cat_speak :speak
  private :dog_speak, :cat_speak

  def initialize
    @speak_to_me = [:cat_speak, :dog_speak].cycle
  end  

  def speak
    send @speak_to_me.next
  end
end

animal = Animal.new
  #=> #<Animal:0x007fe3a222b0e0 @speak_to_me=#<Enumerator:
  #     [:cat_speak, :dog_speak]:cycle>> 

animal.speak #-> meow
animal.speak #-> woof woof
animal.speak #-> meow
animal.speak #-> woof woof