有没有办法在ruby中使实例变量“私有”(C ++或Java定义)?换句话说,我想跟随代码导致错误。
class Base
def initialize()
@x = 10
end
end
class Derived < Base
def x
@x = 20
end
end
d = Derived.new
像Ruby中的大多数东西一样,实例变量并不是真正的“私有”,任何拥有d.instance_variable_get :@x
的人都可以访问它们。
但是,与Java / C ++不同,Ruby中的实例变量始终是私有的。它们永远不会像方法那样属于公共API,因为只能使用那个详细的getter来访问它们。因此,如果您的API中有任何理智,您不必担心有人滥用您的实例变量,因为他们将使用这些方法。 (当然,如果有人想要疯狂并访问私有方法或实例变量,则无法阻止它们。)
唯一的问题是,如果有人在扩展您的课程时意外覆盖了实例变量。这可以通过使用不太可能的名称来避免,也许在你的例子中称之为@base_x
。
切勿直接使用实例变量。只使用访问者。您可以通过以下方式将阅读器定义为public和writer私有:
class Foo
attr_reader :bar
private
attr_writer :bar
end
但是,请记住,private
和protected
并不意味着你认为他们的意思。可以针对任何接收者调用公共方法:命名,自我或隐式(x.baz
,self.baz
或baz
)。受保护的方法只能通过自我或隐式接收器调用(self.baz
,baz
)。私有方法只能使用隐式接收器(baz
)调用。
长话短说,你从非Ruby的角度来看待问题。始终使用访问器而不是实例变量。使用public
/ protected
/ private
记录您的意图,并假设您的API的消费者是负责任的成年人。
有可能(但不建议)完全按照你的要求去做。
期望行为有两个不同的元素。第一个是将x
存储为只读值,第二个是保护getter不被子类中的更改。
只读值
Ruby中可以在初始化时存储只读值。为此,我们使用Ruby块的闭包行为。
class Foo
def initialize (x)
define_singleton_method(:x) { x }
end
end
x
的初始值现在被锁定在我们用来定义getter #x
的块中,除了调用foo.x
之外永远无法访问它,并且它永远不会被改变。
foo = Foo.new(2)
foo.x # => 2
foo.instance_variable_get(:@x) # => nil
请注意,它不是作为实例变量@x
存储的,但它仍然可以通过我们使用define_singleton_method
创建的getter来获得。
保护吸气剂
在Ruby中,几乎任何类的任何方法都可以在运行时被覆盖。有一种方法可以使用method_added
钩子来防止这种情况。
class Foo
def self.method_added (name)
raise(NameError, "cannot change x getter") if name == :x
end
end
class Bar < Foo
def x
20
end
end
# => NameError: cannot change x getter
这是一种非常严厉的保护吸气剂的方法。
它要求我们将每个受保护的getter单独添加到method_added
钩子中,即使这样,您还需要为method_added
及其子类添加另一级别的Foo
保护,以防止编码器覆盖method_added
方法本身。
最好接受一个事实,即在运行时代码替换是使用Ruby时的事实。
与具有不同可见性级别的方法不同,Ruby实例变量始终是私有的(来自对象外部)。但是,内部对象实例变量始终可以从父级,子级或包含的模块访问。
由于可能无法改变Ruby访问@x
的方式,我认为你无法控制它。编写@x
只会直接选择该实例变量,并且因为Ruby不提供对变量的可见性控制,所以我想它是可靠的。
正如@marcgg所说,如果您不希望派生类触及您的实例变量,请不要使用它或找到一种聪明的方法来隐藏它不被派生类看到。
不可能做你想要的,因为实例变量不是由类定义,而是由对象定义。
如果使用组合而不是继承,那么您不必担心覆盖实例变量。
我知道这是旧的,但我遇到了一个我没有想要阻止访问@x的情况,我确实希望将它从任何使用反射进行序列化的方法中排除。具体来说,我经常使用YAML::dump
进行调试,在我的情况下,@ x是Class
类,YAML::dump
拒绝转储。
在这种情况下,我考虑了几种选择
def to_yaml_properties
super-["@x"]
end
但是如果其他翻斗车(to_xml
?)不开心的话,这只会对yaml有效def instance_variables
super-["@x"]
end
因此,虽然这些可能不是OP所说的他所需要的,如果其他人在寻找要从列表中排除的变量而不是访问时找到此帖子,那么这些选项可能是有价值的。