Ruby中确实没有等效的构造。
然而,看起来你正在犯一个经典的移植错误:你在语言A中有一个解决方案,并试图将其翻译成语言B,而你真正应该做的是找出问题,然后弄清楚如何在语言B中解决它。
我真的不能确定你试图从那个小代码nippet中解决的问题是什么,但是这里有一个可能的想法,如何在Ruby中实现它:
class DeviceController
class << self
def my_public_device; @my_public_device ||= Device['mydevice'] end
private
def my_private_device; @my_private_device ||= Device['mydevice'] end
end
end
这是另一个:
class DeviceController
@my_public_device ||= Device['mydevice']
@my_private_device ||= Device['mydevice']
class << self
attr_reader :my_public_device, :my_private_device
private :my_private_device
end
end
(不同之处在于第一个示例是惰性的,它仅在首次调用相应的属性读取器时才初始化实例变量。第二个在执行类体后立即初始化它们,即使它们从不需要,就像Java版本一样。
让我们在这里回顾一些概念。
在 Ruby 中,就像在所有其他“proper”(对于“proper”的各种定义)中一样,面向对象的语言,状态(实例变量、字段、属性、槽、属性,无论你想调用它们什么)始终是私有的。没有办法从外面访问它们。与对象通信的唯一方法是向其发送消息。
[注意:每当我写“没有办法”,“总是”,“唯一的方式”等东西时,它实际上并不意味着“没有方法,除了反思”。例如,在这种特殊情况下,有 。Object#instance_variable_set
换句话说:在 Ruby 中,变量始终是私有的,访问它们的唯一方法是通过 getter 和/或 setter 方法,或者,正如它们在 Ruby 中所说的那样,是属性读取器和/或编写器。
现在,我继续写实例变量,但在Java示例中,我们有静态字段,即类变量。好吧,在Ruby中,与Java不同,类也是对象。它们是类的实例,因此,就像任何其他对象一样,它们可以具有实例变量。因此,在 Ruby 中,类变量的等效变量实际上只是一个标准实例变量,它属于恰好是一个类的对象。Class
(还有类层次结构变量,用双引号表示。这些真的很奇怪,你可能应该忽略它们。类层次结构变量在整个类层次结构中共享,即它们所属的类,其所有子类及其子类及其子类...以及所有这些类的所有实例。实际上,它们更像是全局变量,而不是类变量。它们实际上应该被称为而不是,因为它们与全局变量的关系比实例变量更紧密。它们并非完全无用,但很少有用。@@sigil
$$var
@@var
所以,我们已经介绍了“字段”部分(Java字段==Ruby实例变量),我们已经涵盖了“公共”和“私有”部分(在Ruby中,实例变量始终是私有的,如果你想让它们公开,请使用公共的getter/setter方法),我们已经涵盖了“静态”部分(Java静态字段==Ruby类实例变量)。“最终”部分呢?
在Java中,“final”只是一种拼写“const”的有趣方式,设计人员避免了这种方式,因为C和C++等语言中的关键字被巧妙地破坏了,他们不想让人们感到困惑。Ruby 确实有常量(以大写字母开头表示)。不幸的是,它们并不是真正恒定的,因为试图修改它们,同时生成警告,实际上是有效的。因此,它们更像是一种约定,而不是编译器强制执行的规则。但是,常量更重要的限制是它们始终是公共的。const
因此,常量几乎是完美的:它们不能被修改(好吧,它们不应该被修改),即它们是,它们属于一个类(或模块),即它们是。但是它们总是,所以不幸的是它们不能用于模拟场。final
static
public
private static final
而这正是思考问题而不是解决方案的一点。你想要什么?您希望声明
- 属于一个类,
- 只能读不写,
- 仅初始化一次,并且
- 可以是私有的,也可以是公共的。
你可以实现所有这些,但方式与Java完全不同:
- 类实例变量
- 不提供 setter 方法,只提供 getter
- 使用 Ruby 的复合赋值仅赋值一次
||=
- getter method
您唯一需要担心的是,您不会分配到任何地方,或者更好的是,根本不访问它。始终使用 getter 方法。@my_public_device
是的,这是实现中的一个漏洞。Ruby通常被称为“成人语言”或“同意的成人语言”,这意味着你不需要让编译器强制执行某些东西,而只是把它们放在文档中,只是相信你的开发人员已经学会了触摸其他人的隐私是不礼貌的......
一种完全不同的隐私方法是函数式语言中使用的方法:使用闭包。闭包是在其词法环境中关闭的代码块,即使在该词法环境超出范围之后也是如此。这种实现私有状态的方法在 Scheme 中非常流行,但最近也被 Douglas Crockford 等人推广为 JavaScript。下面是 Ruby 中的一个示例:
class DeviceController
class << self
my_public_device, my_private_device = Device['mydevice'], Device['mydevice']
define_method :my_public_device do my_public_device end
define_method :my_private_device do my_private_device end
private :my_private_device
end # <- here the variables fall out of scope and can never be accessed again
end
请注意我的答案顶部版本的微妙但重要的区别:缺少sigil。在这里,我们创建的是局部变量,而不是实例变量。一旦类体结束,这些局部变量就会超出范围,并且再也无法访问。只有定义两个 getter 方法的两个块仍然可以访问它们,因为它们在类体上关闭。现在,它们确实是私有的,并且它们是 ,因为整个程序中唯一仍然可以访问它们的是纯粹的getter方法。@
final
这可能不是Ruby的惯用语,但对于任何有Lisp或JavaScript背景的人来说,它应该足够清楚。它也非常优雅。