如何允许从 ruby​​ 中的特定类初始化一个类,而其他任何地方都没有?

Posted

技术标签:

【中文标题】如何允许从 ruby​​ 中的特定类初始化一个类,而其他任何地方都没有?【英文标题】:How to allow initializing a class from a particular class in ruby and nowhere else? 【发布时间】:2020-09-28 10:02:29 【问题描述】:
module A
  module B
    class Foo
      def initialize(args)
        @args = args
      end
      def call
        puts 'Inside Foo'
      end
    end
  end
end

module C
  class Boo
    def initialize(args)
      @args = args
    end
    def call
      puts 'Inside Boo'
      A::B::Foo.new(@args).call
    end
  end
end

boo = C::Boo.new(nil).call
# Inside Boo
# Inside Foo

A::B::Foo.new(nil).call
# Inside Foo

如何避免 A::B::Foo.new(nil).call ? 它应该只能从 Boo 类访问。

如果有人想访问Foo 类,他们将能够从Boo 访问它。 我怎样才能做到这一点?

搜索了互联网,但找不到应该称之为这个概念的东西?

【问题讨论】:

你可以将A::B::Foo.new设为private(并使用sendBoo中调用它) 你为什么要这样做? 我们在class Boo 中有一组验证,我无法在class Foo 中添加。所以,我不希望任何人在没有这些验证的情况下使用class Boo 【参考方案1】:

这是 ruby​​ - 所以不存在将对象设为“私有”的铁定方法。 (例如,您可以通过.send! 访问私有方法!)但您至少可以定义一个私有接口

但是,从 OOP 的角度来看,您的界面实际上并没有多大意义。为什么A::B::Foo 只能在C::Boo 内访问? 如果A::B::Foo 是私有的,那么它应该只能在A::B 内访问,而不能在其他任何地方访问。

你要找的关键方法是:private_constant

您可以使用const_get 来规避私有常量查找异常。

因此,我们可以“破解”您当前的实现,使其按如下方式工作:

module A
  module B
    class Foo
      def initialize(args)
        @args = args
      end
      def call
        puts 'Inside Foo'
      end
    end
    private_constant :Foo
  end
end


module C
  class Boo
    def initialize(args)
      @args = args
    end
    def call
      puts 'Inside Boo'
      A::B.const_get(:Foo).new(@args).call
    end
  end
end

# This works:
C::Boo.new(nil).call

# This errors:
A::B::Foo.new(nil).call

【讨论】:

同样,您可以通过将A::B::new 设为私有方法来实现上述目的。但是我建议使用private_constant,因为实际上在某些上下文中初始化这些对象似乎很好......我只是认为说“你只能在C中初始化它们”在架构上没有多大意义! *A::B::Foo::new 从您将 C 视为工厂的角度来看,这是有道理的,并且您不希望其他类能够创建 A::B,除非它们使用 C 的接口。例如,这在 C# 和 Java 等语言中使用“internal”关键字。 (有趣的故事,因为我来自 C# 背景,我不久前就问过这个问题,我的问题被标记为重复。人们完全没有抓住重点) @cesartalves 啊,我以前没听说过这个... C# 中有一个InternalVisiblesTo 方法,很有趣!这也与 C++ 中的“朋友类”功能非常相似。 Ruby 并没有“朋友类”的概念。我确实找到了添加功能的this gem,虽然我还没有尝试过!

以上是关于如何允许从 ruby​​ 中的特定类初始化一个类,而其他任何地方都没有?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 与 Python 元类的类比是啥?

无法将一个类包含到 Ruby 中的另一个类中:未初始化的常量 (NameError)

这里的这一属性如何在 ruby​​ 类中包含多个属性?

Ruby TracePoint:如何捕获特定类的定义?

如何从 Ruby 中的实例方法访问受保护的类方法?

哪些 Ruby 类支持 .clone?