Ruby 中确实没有等效的构造。
然而,看起来您正在犯一个经典的移植错误:您有一个solution并尝试将其翻译成语言 B,而你真正应该做的是弄清楚problem然后想办法用B语言解决这个问题。
我无法确定您要从那个小代码片段中解决的问题是什么,但这是one如何在 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 中,就像在所有其他“适当的”(对于“适当的”的各种定义)面向对象语言中一样,状态(实例变量、字段、属性、槽、属性,无论你想如何称呼它们)都是always私人的。有no way从外部访问它们。与对象通信的唯一方法是向其发送消息。
[注:每当我写“没办法”、“总是”、“唯一的办法”等之类的东西时,它实际上并不意味着“没有办法,除了反思”。在这种特殊情况下,有Object#instance_variable_set
, 例如。]
换句话说:在 Ruby 中,变量始终是私有的,访问它们的唯一方法是通过 getter 和/或 setter 方法,或者如 Ruby 中所称,属性读取器和/或写入器。
现在,我继续写关于实例变量,但在 Java 示例中我们有静态字段, i.e. class变量。嗯,在 Ruby 中,与 Java 不同,类也是对象。他们是Class
类等等,就像任何其他对象一样,它们可以有实例变量。因此,在 Ruby 中,类变量的等价物实际上只是一个标准实例变量,它属于一个恰好是类的对象。
(还有类层次结构变量,用双 at 符号表示@@sigil
。这些真的很奇怪,你应该忽略它们。类层次结构变量在整个类层次结构中共享,即它们所属的类、其所有子类及其子类及其子类……以及所有这些类的所有实例。实际上,它们更像是全局变量而不是类变量。他们真的应该被称为$$var
代替@@var
,因为它们与全局变量的关系比实例变量更密切。它们并非完全无用,只是很少有用。)
所以,我们已经介绍了“字段”部分(Java 字段== Ruby 实例变量),我们已经介绍了“公共”和“私有”部分(在 Ruby 中,实例变量始终是私有的,如果你想将它们设为公共,使用公共 getter/setter 方法)并且我们已经介绍了“静态”部分(Java 静态字段 == Ruby 类实例变量)。那么“最后”部分呢?
在 Java 中,“final”只是“const”的一种有趣的拼写方式,设计者避免使用这种方式,因为const
C 和 C++ 等语言中的关键字被巧妙地破坏了,他们不想让人们感到困惑。红宝石does有常量(以大写字母开头表示)。不幸的是,它们并不是真正恒定的,因为尝试修改它们,同时生成警告,实际上是有效的。因此,它们更多的是一种约定,而不是编译器强制执行的规则。然而,常量更重要的限制是它们始终是公共的。
所以,常量几乎是完美的:它们不能被修改(嗯,它们不应该被修改),即它们是final
,它们属于一个类(或模块),即它们是static
。但他们总是public
,所以不幸的是它们不能用于建模private static final
fields.
这正是思考问题而不是解决方案的关键所在。你想要什么?你想要说明的是
- 属于一个类,
- 只能读不能写,
- 仅初始化一次并且
- 可以是私有的或公共的。
您可以实现所有这些,但方式与 Java 完全不同:
- 类实例变量
- 不提供setter方法,只提供getter方法
- 使用 Ruby 的
||=
复合赋值仅赋值一次
- 获取方法
您唯一需要担心的是,您没有分配给@my_public_device
任何地方,或者更好的是,根本不访问它。始终使用 getter 方法。
是的,这个is实施中存在漏洞。 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
请注意我的答案顶部的版本之间微妙但重要的区别:缺乏@
印记。在这里,我们正在创造local变量,不instance变量。一旦类主体结束,这些局部变量就会超出范围,并且永远无法再次访问。Only定义两个 getter 方法的两个块仍然可以访问它们,因为它们关闭了类主体。现在,他们是really私人的and他们是final
,因为整个程序中唯一仍然可以访问它们的是纯getter method.
这可能不是 Ruby 惯用的做法,但对于任何有 Lisp 或 JavaScript 背景的人来说都应该足够清楚了。它也非常优雅。