3

这是我的代码的样子:

module A
  def foo
    puts "A"
  end
end

module B
  include A
  def bar
    foo
  end
end

class MyClass
  include B
  def foo
    puts "X"
  end
  def self.test
    puts bar
  end
end

当我调用“C.test”时,我得到的是“X”而不是“A”(这是我想要的),因为 foo 的本地定义已经覆盖了 A 中的定义。我无法更改任何一个 foo 的签名。我只能主要编辑自己的类;我可以编辑模块 A 和 B,但许多现有代码都使用它们并且它们是(例如,没有将 foo 更改为 A.foo)。我正在考虑做

class MyClass
  module MyModules
    include B
  end
  ....
    MyModules.bar
  ....
end

但这不起作用。

如何在包含 B 时“本地化”命名空间?

4

2 回答 2

3

据我了解,您想在内部覆盖A#foo(也被. 但是,您只想在内部覆盖它,而不是在mixin 中的代码。B#barMyClassMyClassB

这里要理解的是,当ABmixins 的代码运行时,它们将成为MyClass实例的一部分。因此,不可能在MyClass不影响 mixins 的情况下覆盖实例方法。就好像 mixins 的方法被扔进一个桶(MyClass实例),然后运行。它们没有单独的范围。因此,简单的答案是:不,你做不到。

不过,可能有几种可能的解决方案,其中许多闻起来像意大利面。遇到这样的问题可能表明整体设计决策需要一些重构。

回顾模块的基础知识:

module Numbered
  DEFAULT = "1234-AWESOME"
  def serial_number
    DEFAULT
  end
  def self.awesome?
    true
  end
end

Numbered.awesome? # => true
Numbered.serial_number # whoops! NoMethodError.

o = Object.new
o.extend(Numbered)
o.serial_number #=> "1234-AWESOME"

Numbered.extend(Numbered)
Numbered.serial_number # => "1234-AWESOME"

class Dog
  extend Numbered
end

Dog.serial_number # => "1234-AWESOME"
Dog::DEFAULT # => "1234-AWESOME"
fido = Dog.new
fido.serial_number # NoMethodError!

这是因为该方法是作为类方法添加的。include救援:

class Fish
  include Numbered

  def serial_number
    super + '-FSH'
  end
end

cod = Fish.new
cod.serial_number # => "1234-AWESOME-FSH"

因此,include将所有方法堆放在一个公共实例中,但我们仍然可以使用它super来调用我们覆盖的包含方法。如果您在意大利面方面寻求解决方案,您可能可以在bar方法内部使用 super ,有条件地调用您覆盖的方法。

此外,根据具体情况,您可以设置一个新模块,extend并使用包含您需要访问的方法的模块,就像您添加的答案一样。

于 2011-07-15T16:22:49.897 回答
1

我找到了我想要的解决方案,可能不是最好的答案,但完全按照我想要的方式工作。

class MyClass
  class MyModules
    extend B
  end
  ....
    MyModules.bar
  ....
end

这有效地允许我限制本地类 MyModules 中 B 和所有其他包含模块的范围。在我的代码中它实际上是有意义的,因为所有包含的模块都处理表达式操作,所以我可以调用本地类“Expr”(他们现在严重过度使用名称“eval”、“bind”和“free”)。

于 2011-07-15T21:12:38.307 回答