Ruby嵌套模块作为命名空间

时间:2013-09-03 06:32:37

标签: ruby oop module namespaces

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

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

现在假设我想在模块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::helper的完整限定名称(B)来呼叫helper。我更喜欢以某种方式告诉Ruby这个名称空间前缀是“默认”,并简称为using A::helper。在C ++中,我可以在命名空间B中编写include A,它可以解决问题。但是如何在Ruby中做到这一点?

我尝试在extend A中添加BA::helper,但这些都不起作用。当这些模块混合在一起时,它们似乎只在类内部工作,但是当它们是独立的时,它们只用于命名空间。

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

哦,还有一件事:
假设一个不同的场景,我希望A只能从A的方法中使用,因为它只是一些实现函数,我在其中考虑了{{{{}内部许多函数使用的一些常用代码。 1}},但现在我不希望外界看到它,只是A的方法。我该怎么办?

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

修改 为什么贬低?我试图尽可能清楚,我需要的是在另一个命名空间内默认一个名称空间,以完全摆脱长的完全限定名称(不仅仅是别名他们的东西更短),而我的问题不涉及课程和对象,只是用于命名空间的普通模块。我怎么解释呢?

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

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

3 个答案:

答案 0 :(得分:9)

好的,因为与我的问题相关的唯一答案已被删除,我将尝试自己回答我的问题,基于@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)。但是,为了使它们可供外部使用而不进行实例化(毕竟模块是不可能的),我extend A模块本身,这使得这些方法也成为它的类方法。但是由于它们也是模块A的实例方法,因此可以从该模块的其他方法中调用它们,而无需为模块名称添加前缀。模块B也是如此,extend本身也使用模块A的方法,使它们成为自己的模块。然后我也可以在B的方法中调用它们而不用前缀,就像我想要的那样。

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

这种方法的唯一问题可能在于它们的接口混合在一起。这在B内并不是什么问题,因为这就是我真正想要的:能够像A的方法一样访问B的方法,而不需要为他们添加完整的资格。所以我得到了我应得的。但这可能会导致外部问题,因为它是B的实现细节,它在内部使用A的方法。它不应泄漏到外面的世界,但确实如此。我将尝试以某种方式使用访问控制来修复它,也许它可能以某种方式。

修改:是的,可以通过在extend AB之后插入以下行来完成:

private_class_method *A.public_instance_methods

这种方式B可以在内部调用A的方法,但无法从外部访问它们。

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

仅使用第三个​​模块通过它代理接口。对我而言,这是一个丑陋的黑客,而不是一个优雅的解决方案,因为它引入了这个额外的模块,这会混淆这样一个库的用户。他们不知道他们是应该使用A还是C,以及为什么要使用这样的装置。仅通过观察它是如何工作的并不明显。它需要一些更彻底的分析才能弄清楚它的真正作用以及为什么以这种方式构建它。这不是一个干净的设计。

另一方面,在我的解决方案中,只有两个模块,如最初设计的那样,并且它们的目的应该对于该库的用户是清楚的。有这个奇怪的extend self,但它似乎是Ruby中比在整个地方传播代理模块更常见的习语。

非常感谢你的尝试。下次尝试不那么骄傲(当你看到有人提出问题时,并不总是这样他是一个菜鸟)并且注意你心爱的One True Language(不要误解我的意思,我喜欢Ruby语言,这很酷,干净,但它也有一些缺点,就像任何一种语言一样,最好是寻求解决它们而不是埋头并假装根本就没有问题,因为它不是语言设计的东西。)

答案 1 :(得分: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

答案 2 :(得分:-2)

我建议重新考虑你的基本过程。这句话是一面红旗:

  

我有一个嵌套的模块结构,仅用于命名空间;没有混合到类中,仅用于命名空间目的;没有混合到班级......

这是使用模块的错误方法,即使你现在不同意,你可能会发现这有更多的经验。我建议更多地研究Ruby继承,以及模块在“野外”使用的方式。网上有很多好的博客,指南和资料,谷歌点击一下......