是否有充分的理由让“私有"以其在Ruby中的方式工作? [英] Are there good reasons for 'private' to work the way it does in Ruby?
问题描述
我花了一段时间才了解 private 方法在Ruby中的工作方式,这确实让我感到非常尴尬.有谁知道以私人方式处理私人方法是否有充分的理由?这仅仅是历史原因吗?还是执行原因?还是有良好的逻辑依据(例如语义)?
It took me a while to understand how private methods work in Ruby, and it really strikes me as being very awkward. Does anyone know if there are good reasons for private methods to be handled the way they are? Is it just historic reasons? Or implementation reasons? Or are there good solid logical reasons (ie. semantic)?
例如:
class Person
private
attr_reader :weight
end
class Spy < Person
private
attr_accessor :code
public
def test
code #(1) OK: you can call a private method in self
Spy.new.code #(2) ERROR: cannot call a private method on any other object
self.code #(3) ERROR!!! cannot call a private method explicitly on 'self'
code="xyz" #(4) Ok, it runs, but it actually creates a local variable!!!
self.code="z" #(5) OK! This is the only case where explicit 'self' is ok
weight #(6) OK! You can call a private method defined in a base class
end
end
- Ruby在第(1),(2)和(5)行的行为似乎是合理的.
- (6)还可以的事实有点奇怪,尤其是来自Java和C ++的事实.有什么充分的理由吗?
- 我真的不明白为什么(3)会失败!一个解释,有人吗?
- 第(4)行中的问题看起来像语法中的模棱两可,与私有"无关.
- Ruby's behaviour on lines (1), (2) and (5) seems reasonable.
- The fact that (6) is ok is a bit strange, especially coming from Java and C++. Any good reason for this?
- I really do not understand why (3) fails ! An explanation, anyone?
- The problem on line (4) looks like an ambiguity in the grammar, which has nothing to do with 'private'.
有什么想法吗?
推荐答案
You might find it helpful to read ruby's definition of public, private and protected. (Skip to Access Control)
Ruby的private类似于Java的protected.没有Ruby等效于Java的private. 现在,该解决方案提供了一种伪造Java理想的Ruby对象私有方法的方法.
Ruby's private is analogous to Java's protected. There is no Ruby equivalent of Java's private. This solution now provides a method of faking it Java's ideal of private in Ruby objects.
私有定义为只能仅被隐式调用的方法/变量.这就是为什么语句2和3失败的原因.换句话说,专用方法/变量在定义它们的类或子类的上下文中进行限制.继承将私有方法传递给子类,因此可以使用隐式的self进行访问. (解释为什么语句6起作用.)
Private is defined as methods/variables that can only be called implicitly. This is why statements 2 and 3 fail. In other words, private limits methods/variables to the context of a class or subclass in which they are defined. Inheritance passes private methods to the subclasses and can therefore be accessed with an implicit self. (Explaining why statement 6 works.)
我认为您正在寻找更受保护的东西.它的行为类似于未获得可见性的Java访问器(例如:public,private,protected) 通过将间谍"中的私有"更改为受保护的您的所有6条语句. 定义类或其子类的任何实例都可以调用受保护的方法.只要调用方是响应该调用的对象的类或从该类继承,则对self进行显式或隐式调用都是对受保护方法的有效语句.
I think you're looking for something closer to protected. Which behaves similarly to Java accessors that are not given a visibility (eg: public, private, protected) By changing the private in Spy to protected all 6 of your statements work. Protected methods can be called by any instance of the defining class or their subclasses. Either explicitly or implicitly called on self are valid statements for protected methods so long as the caller is either the class of the object responding to the call, or inherits from it.
class Person
private
attr_reader :weight
end
class Spy < Person
protected
attr_accessor :code
public
def test
code #(1) OK: you can call a private method in self
Spy.new.code #(2) OK: Calling protected method on another instance from same class family or a descendant.
self.code #(3) OK: Calling protected method on with explicit self is allowed with protected
code="xyz" #(4) Ok, it runs, but it actually creates a local variable!!!
self.code="z" #(5) OK! This is the only case where explicit 'self' is ok
weight #(6) OK! You can call a private method defined in a base class
end
end
s = Spy.new
s.test # succeeds
s.code #(7) Error: Calling protected method outside of the class or its descendants.
至于陈述4.假设这是为了避免歧义,您是正确的.这更是对红宝石动态特性潜在危害的一种保障.它可以确保您不能在以后再次打开类时覆盖访问器.可能会发生这种情况,例如,评估受污染的代码.
As for statement 4. You are correct in assuming this is to avoid ambiguity. It's more a safeguard to the potential harm of ruby's dynamic nature. It ensures that you cannot override accessors by opening up the class again later. A situation that can arise, for example by eval'ing tainted code.
我只能推测他的设计决定会导致这些行为.对于大多数情况,我认为这取决于语言的动态性质.
I can only speculate on he design decisions that led to these behaviours. For most of it I feel it comes down to the dynamic nature of the language.
P.S.如果您真的想给事物提供java私有定义.仅可用于定义它的类,甚至子类也不可用.您可以在类中添加一个self.inherited方法,以删除对您要限制访问的方法的引用.
P.S. If you really want to give things the java definition of private. Only available to the class in which it's defined, not even subclasses. You could add an self.inherited method to your classes to remove references to the methods you want to limit access to.
使权重属性无法从子类访问:
Making the weight attribute inaccessible from subclasses:
class Person
private
attr_reader :weight
def initialize
@weight = 5
end
def self.inherited(subclass)
subclass.send :undef_method, :weight
end
end
class Spy < Person
private
attr_accessor :code
public
def test
weight
end
end
Person.new.send(:weight) # => 5
Spy.new.send(:weight) #=> Unhelpful undefined method error
将undef_method调用替换为以下内容可能更有意义:
It may make more sense to replace the undef_method call to something like this:
def self.inherited(subclass)
subclass.class_eval %{
def weight
raise "Private method called from subclass. Access Denied"
end
}
end
哪个提供了更有用的错误和相同的功能.
Which provides a much more helpful error and the same functionality.
发送对于绕开为其他类调用私有方法是必需的.仅用于证明事情确实有效.
The send is necessary to get around calling private methods for other classes. Only used to prove that things are actually working.
事后看来,使私有和受保护的东西毫无用处.如果您真的很想保护自己的方法,则必须重写send来阻止它们.以下代码根据对象的private_methods实现此目的:
Which in hindsight, makes private and protected useless. If you're really serious about protecting your methods you will have to override send to block them. The following code does that based on the private_methods of the object:
def send_that_blocks_private_methods(method, *args)
if private_methods.include?(method.to_s)
raise "Private method #{method} cannot called be called with send."
else
send_that_allows_private_methods(method, *args)
end
end
alias_method :send_that_allows_private_methods, :send
alias_method :send, :send_that_blocks_private_methods
private :send_that_allows_private_methods
您可以指定要阻止访问的private_method的class_variable,而不是拒绝访问所有私有方法.您也可以将send设为私有,但是从对象外部调用send有合法用途.
You could specify a class_variable of private_methods you want to block access to instead of denying access to all private methods. You could also make send private, but there are legitimate uses of calling send from outside an object.
这篇关于是否有充分的理由让“私有"以其在Ruby中的方式工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!