如何允许从 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(并使用send
从Boo
中调用它)
你为什么要这样做?
我们在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 中的特定类初始化一个类,而其他任何地方都没有?的主要内容,如果未能解决你的问题,请参考以下文章