Ruby 将模块嵌套为命名空间



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

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

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

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

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

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

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

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

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

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

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

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

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

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

private_class_method *A.public_instance_methods

这样B可以在内部调用A的方法,但不能从外部世界访问它们。

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

一直在使用第三个模块,只是通过它代理接口。对我来说,这是一个丑陋的黑客,而不是一个优雅的解决方案,因为它引入了这个额外的模块,这会让这样一个库的用户感到困惑。他们不知道他们应该使用A还是C,以及为什么要使用这样的装置。仅仅看一眼就看不出它是如何工作的。它需要一些更彻底的分析来弄清楚它到底做了什么以及为什么它以这种方式构建。这不是一个干净的设计。

另一方面,在我的解决方案中,只有两个模块,正如最初设计的那样,它们的用途对于该库的用户来说应该是明确的。有这个奇怪的extend self,但它仍然是Ruby中比到处传播代理模块更常见的成语。

所以谢谢大家的尝试。下次试着不那么傲慢(当你看到有人问一个问题时,他并不总是一个菜鸟(并专注于你心爱的 One True Language(不要误会我的意思,我喜欢 Ruby 语言,它很酷很干净,但它也有一些缺点,就像任何语言一样,最好寻求解决它们,而不是埋头假装根本没有问题, 因为这不是语言的设计目的(。

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

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

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

这是使用模块的错误方式,即使您现在不同意,您也可能会通过更多经验发现这一点。我建议更多地研究 Ruby 继承,以及模块在"野外"的使用方式。网上有大量好的博客、指南和材料,谷歌点击一下......

最新更新