在 Ruby 中的模块中包含模块时如何解决命名空间冲突?

发布于 2024-11-23 17:30:07 字数 551 浏览 1 评论 0原文

这是我的代码的样子:

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

但这不起作用。

在执行 include B 时如何“本地化”命名空间?

Here is what my code looks like:

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

When I call "C.test" I get "X" instead of "A" (which is what I want) because the local definition of foo has overridden that in A. I can't change the signature of either foo's. I can only mainly edit my own class; I can edit modules A and B but lots of existing code use them and they are (so no changing foo to A.foo for instance). I am thinking of doing

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

But this does not work.

How can I "localize" the namespace when doing include B?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

狂之美人 2024-11-30 17:30:07

据我了解,您想要覆盖 MyClass 内的 A#fooB#bar 也使用它)。但是,您只想在 MyClass 内重写它,而不是 B mixin 中的代码。

这里需要理解的是,当 AB mixin 的代码运行时,它们将成为 MyClass 实例的一部分。因此,不可能在不影响 mixin 的情况下重写 MyClass 的实例方法。这就好像 mixin 的方法被扔进一个桶(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,有条件地调用您要重写的方法。

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

From what i understand, you want to override A#foo (which is also used by B#bar) inside MyClass. However, you only want to override it inside MyClass, and not for the code in the B mixin.

The thing to understand here, is that when the code of the A and B mixins are run, they will be part of a MyClass instance. Therefore, it is impossible to override the instance methods of MyClass without affecting the mixins. It is as if the methods of the mixins were thrown into a bucket (the MyClass instance), and then run. They do not have separate scopes. Therefore, the simple answer is: no, you can not do it.

There could be several possible solutions though, many of which smell like pasta. Running into problems like this one could be an indication that the overall design decisions need some refactoring.

Going over the basics of modules:

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!

This is because the method was added as a class method. include to the rescue:

class Fish
  include Numbered

  def serial_number
    super + '-FSH'
  end
end

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

So, include heaps all the methods together into one common instance, but we can still use super to call an included method that we were overriding. If you go for a solution on the pasta side, you might be able to use super inside the bar method, conditionally invoking the method you were overriding.

Also, depending on the case, you could set up a new module and extend it with the module containing the methods you need access too, like in the answer you added.

狼性发作 2024-11-30 17:30:07

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

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

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

I found a solution for what I wanted, might not be the best answer but works exactly the way I want it to.

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

This effectively allows me to limit the scope of B and all other included modules in the local class MyModules. In my code it actually has a meaning because all the included modules handle expression manipulation so I can call the local class "Expr" (and they are seriously overusing the names 'eval', 'bind' and 'free' right now).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文