pra*_*dvk 34 ruby instance-variables private-members
有没有办法在ruby中使实例变量"私有"(C++或Java定义)?换句话说,我希望以下代码导致错误.
class Base
def initialize()
@x = 10
end
end
class Derived < Base
def x
@x = 20
end
end
d = Derived.new
Run Code Online (Sandbox Code Playgroud)
Jos*_*Lee 35
像Ruby中的大多数东西一样,实例变量并不是真正的"私有",任何人都可以访问它们d.instance_variable_get :@x.
但是,与Java/C++不同,Ruby中的实例变量始终是私有的.它们永远不会像方法那样属于公共API,因为只能使用那个详细的getter来访问它们.因此,如果您的API中有任何理智,您不必担心有人滥用您的实例变量,因为他们将使用这些方法.(当然,如果有人想要疯狂并访问私有方法或实例变量,则无法阻止它们.)
唯一的问题是,如果有人在扩展您的课程时意外覆盖了实例变量.这可以通过使用不太可能的名称来避免,也许@base_x在您的示例中调用它.
Ste*_*set 26
切勿直接使用实例变量.只使用访问者.您可以通过以下方式将阅读器定义为public和writer私有:
class Foo
attr_reader :bar
private
attr_writer :bar
end
Run Code Online (Sandbox Code Playgroud)
但是,请记住,private而protected并不意味着什么,你认为他们的意思.公共方法可以称为对任何接收器:命名,自我,或隐式的(x.baz,self.baz,或baz).保护的方法可仅具有自的接收器或隐式地(称为self.baz,baz).私有方法只能使用隐式receiver(baz)调用.
长话短说,你从非Ruby的角度来看待问题.始终使用访问器而不是实例变量.使用public/ protected/ private记录您的意图,并假设您的API的消费者是负责任的成年人.
use*_*951 13
有可能(但不建议)完全按照你的要求去做.
期望行为有两个不同的元素.第一个是以只读值存储x,第二个是保护getter不被子类更改.
只读值
Ruby中可以在初始化时存储只读值.为此,我们使用Ruby块的闭包行为.
class Foo
def initialize (x)
define_singleton_method(:x) { x }
end
end
Run Code Online (Sandbox Code Playgroud)
现在,初始值x被锁定在我们用来定义getter的块中,#x除了通过调用之外永远不会被访问foo.x,并且它永远不会被改变.
foo = Foo.new(2)
foo.x # => 2
foo.instance_variable_get(:@x) # => nil
Run Code Online (Sandbox Code Playgroud)
请注意,它不是作为实例变量存储的@x,但它仍然可以通过我们创建的getter使用define_singleton_method.
保护吸气剂
在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
Run Code Online (Sandbox Code Playgroud)
这是一种非常严厉的保护吸气剂的方法.
它要求我们将每个受保护的getter method_added单独添加到钩子中,即使这样,您还需要为其及其子类添加另一级别的method_added保护,Foo以防止编码器覆盖该method_added方法本身.
最好接受一个事实,即在运行时代码替换是使用Ruby时的事实.
与具有不同可见性级别的方法不同,Ruby实例变量始终是私有的(来自对象外部).但是,内部对象实例变量始终可以从父级,子级或包含的模块访问.
由于可能无法改变Ruby的访问方式@x,因此我认为您无法控制它.编写@x只会直接选择该实例变量,并且因为Ruby不提供对变量的可见性控制,所以我想它是可靠的.
正如@marcgg所说,如果您不希望派生类触及您的实例变量,请不要使用它或找到一种聪明的方法来隐藏它不被派生类看到.