16

我有一个嵌套的模块结构,仅用于命名空间;没有混入类等。所以我有这样的代码:

module Lib

  module A
    def A.foo
      puts 'Lib::A::foo'
    end
  end

  module B
    def B.bar
      puts 'Lib::B::bar'
    end
  end

end

现在假设我想在 module 中再添加一个辅助方法A,并且我希望两个模块都可以轻松使用它。我得到以下信息:

module Lib

  module A
    def A.foo
      puts 'Lib::A::foo'
    end
    def A.helper
      puts 'Lib::A::helper'
    end
  end

  module B
    def B.bar
      puts 'Lib::B::bar'
      A::helper
    end
  end

end

它似乎工作,但它有一些我想摆脱的缺点:
我不想一直从内部调用helper它的完整限定名(A::helperB。我更愿意以某种方式告诉 Ruby 这个命名空间前缀是“默认的”,并简单地将其称为helper. 在 C++ 中,我可以using A::helper在命名空间内编写B,它会解决问题。但是如何在 Ruby 中做到这一点?

我尝试添加include Aand extend Ainside B,但它们都不起作用。它们似乎只在类中工作,当这些模块混合在一起时,但当它们是独立的,仅用于命名空间时则不行。

还有其他方法可以让它按我想要的方式工作吗?

哦,还有一件事:
假设一个不同的场景,我希望A::helper只从 insideA的方法中使用,因为它只是一些实现函数,我在其中分解出一些内部许多函数使用的通用代码A,但现在我不'不希望它对外界可见,只是对A'方法。我该怎么做?

我尝试使用module_function+A.从所有其他应该隐藏的函数中删除前缀,但是对于 中的其他方法,它们也被隐藏了A,因为它们是实例方法,并且无法实例化模块。那么,我怎样才能对外界隐藏一个模块方法,并且仍然允许其他模块方法在内部使用它呢?

编辑 为什么要投票?我试图尽可能清楚,我需要在另一个名称空间中默认一个名称空间以完全摆脱长的完全限定名称(不仅仅是将它们别名为更短的名称),并且我的问题不关心类和对象,只是用于命名空间目的的普通模块。我还能怎么解释呢?

命名空间机制在 Ruby中似乎并没有像 C++ 等语言那样在本机中得到完全支持,这不是我的错,而且这似乎只是使用模块和类来拥有真正命名空间的某些功能的副作用(它们像鸭子一样嘎嘎,它们有鸭嘴,但它们是鸭嘴兽,而不是鸭子,不应该被宣传为它们显然不是的命名空间,因为它只会混淆从其他语言进入 Ruby 的人与真正的命名空间),也不如果它们在 Ruby 中不容易实现,那么您显然不理解其他编程语言的概念(因为我看到您似乎假装我的问题不存在并恢复到您可以解决的问题用 Ruby 轻松实现;但这不是我需要的)。

为什么与我的问题相关的唯一答案已被删除?不酷;。

4

3 回答 3

13

好的,由于与我的问题相关的唯一答案已被删除,我将尝试自己回答我的问题,基于@sawa 删除的答案(感谢@sawa 提示我正确的方向)。我对其进行了一些修改,以更好地满足我的需求并更优雅。稍后我将描述为什么原始@sawa 的答案不是我想要的。
好的,所以事不宜迟,这是我自己的解决方案尝试:

module Lib

  module A
    extend self
    def foo
      puts 'Lib::A::foo'
    end
    def helper
      puts 'Lib::A::helper'
      foo
    end
  end

  module B
    extend A
    def self.bar
      puts 'Lib::B::bar'
      helper
    end
  end

end

puts 'Calling Lib::A::foo:'
Lib::A::foo        # => Lib::A::foo

puts 'Calling Lib::A::helper:'
Lib::A::helper     # => Lib::A::helper; Lib::A::foo

puts 'Calling Lib::B::bar:'
Lib::B::bar        # => Lib::B::bar; Lib::A::helper; Lib::A::foo

它是这样工作的:
首先,它将所有方法定义为特定模块类本身的实例方法(A在本例中)。但是,为了使它们在不实例化的情况下可供外部使用(毕竟这对于模块来说是不可能的),我extendA模块与自身结合起来,这使得这些方法也成为它的类方法。但是由于它们也是模块的实例方法A,因此可以从该模块的其他方法内部调用它们,而无需在它们前面加上模块名称。module 也是如此B,它extend本身也带有 moduleA的方法,使它们成为他自己的。然后我也可以在B's 方法中调用它们而无需前缀,就像我想要的那样。

正如你所看到的,我不仅可以从外部调用两个模块的方法,就好像它们是它们的类的方法一样,而且我可以在没有完全限定其名称的情况下调用A::helperfrom ,而且我也可以在没有限定的情况下调用from 。这正是我所需要的,并且似乎按我的预期工作。B::fooA::fooA::helper

这种方法的唯一问题可能是它们的接口混合在一起。这在内部不是什么大问题B,因为这正是我真正想要的:能够像访问A's 方法一样访问 'sB方法,而无需在它们前面加上完整的限定词。所以我得到了我应得的。但这可能会从外部引起问题,因为它是B它在内部使用A's 方法的实现细节。它不应该泄漏到外部世界,但确实如此。我会尝试通过访问控制以某种方式修复它,也许它会以某种方式成为可能。

编辑:是的,可以通过在extend Ain之后插入以下行来完成B

private_class_method *A.public_instance_methods

这种方式B可以在内部调用A's 方法,但不能从外部访问。

现在原始@sawa 的解决方案出了什么问题:

它一直在使用第三个模块来通过它代理接口。对我来说,这是一个丑陋的 hack ,而不是一个优雅的解决方案,因为它引入了这个额外的模块,这会使此类库的用户感到困惑。他们将不知道是否应该使用Aor C,以及为什么要使用这样的装置。仅仅通过观察它并不清楚它是如何工作的。它需要一些更彻底的分析来弄清楚它的真正作用以及为什么要这样构造。这不是一个干净的设计。

另一方面,在我的解决方案中,最初设计的只有两个模块,并且对于该库的用户来说,它们的目的应该很清楚。这很奇怪extend self,但它似乎仍然是 Ruby 中的一个更常见的习惯用法,而不是到处传播代理模块。

所以感谢你们的尝试。下次尽量不要那么自大(当你看到有人问问题时,他并不总是菜鸟)并专注于你心爱的一种真正的语言(不要误会我的意思,我喜欢 Ruby 语言,它很酷,而且干净,但与任何语言一样,它也有一些缺点,最好寻求解决它们,而不是埋头苦干假装根本没有问题,因为这不是该语言的设计目标)。

于 2013-09-03T10:08:02.170 回答
0
module E
  module A
    def foo
      puts 'E::A::foo'
    end
    def helper
        puts 'E::A::helper'
    end    
  end

  module B
    extend A
    def B.bar
      puts 'E::B::bar'
      helper
    end
  end
end

E::B.bar #=> E::B::bar & E::A::helper
于 2013-09-03T06:43:46.670 回答
-5

我建议重新考虑你的基本过程。这句话是一个危险信号:

我有一个嵌套的模块结构,仅用于命名空间;不得混入仅用于命名空间的类等;没有混入课堂...

这是使用 Modules 的错误方式,即使您现在不同意,您可能会通过更多经验发现这一点。我建议更多地研究 Ruby 继承,以及“在野外”使用模块的方式。网上有大量优秀的博客、指南和资料,只需 google 一下...

于 2013-09-03T08:06:02.003 回答