为什么Ruby有私有和受保护的方法?
在阅读本文之前,我认为Ruby中的访问控制是这样工作的:
-
public
– 可以被任何对象访问(例如Obj.new.public_method
) -
protected
– 只能从对象本身以及任何子类访问 -
private
– 与保护一样,但该方法不存在于子类中
然而,看起来, protected
和private
行为是相同的,除了你不能用一个明确的接收者调用private
方法(即self.protected_method
工作,但self.private_method
不)。
这是什么意思? 什么时候有一种情况,当你不希望你的方法调用一个明确的接收器?
protected
方法可以由定义类或其子类的任何实例调用。
private
方法只能从调用对象中调用。 您不能直接访问另一个实例的私有方法。
这是一个快速实用的例子:
def compare_to(x) self.some_method <=> x.some_method end
some_method
在这里不能private
。 它必须受到protected
因为您需要它来支持显式接收器。 你典型的内部帮手方法通常可以是private
因为他们永远不需要像这样调用。
需要注意的是,这与Java或C ++的工作方式不同。 Ruby中的private
类似于Java / C ++中的protected
,因为子类可以访问该方法。 在Ruby中,没有办法像在Java中使用private
方法那样限制从其子类访问方法。
Ruby中的可见性在很大程度上是一个“推荐”,因为你总是可以使用send
来访问一个方法:
irb(main):001:0> class A irb(main):002:1> private irb(main):003:1> def not_so_private_method irb(main):004:2> puts "Hello World" irb(main):005:2> end irb(main):006:1> end => nil irb(main):007:0> foo = A.new => #<A:0x31688f> irb(main):009:0> foo.send :not_so_private_method Hello World => nil
区别
- 任何人都可以调用你的公共方法
- 你可以调用你的保护方法, 或者你的类(或后代类)的其他成员可以从外部调用你的保护方法。 没有人可以。
- 只有你可以调用你的私有方法,因为它们只能被隐式的
self
接收者调用。 即使你不能调用self.some_private_method
; 你必须自己调用private_method
。 ( iGEL指出:“但是有一个例外,如果你有一个私有方法age =,你可以(而且必须)用self调用它来将它从局部variables中分离出来。
在Ruby中,这些区别只是从一个程序员到另一个程序员的build议。 非公开的方法是一种说法:“我保留改变这个权利的权利,不要依赖它”。 但是你仍然可以得到锋利的send
剪刀,并可以调用任何你喜欢的方法。
一个简短的教程
# dwarf.rb class Dwarf include Comparable def initialize(name, age, beard_strength) @name = name @age = age @beard_strength = beard_strength end attr_reader :name, :age, :beard_strength public :name private :age protected :beard_strength # Comparable module will use this comparison method for >, <, ==, etc. def <=>(other_dwarf) # One dwarf is allowed to call this method on another beard_strength <=> other_dwarf.beard_strength end def greet "Lo, I am #{name}, and have mined these #{age} years.\ My beard is #{beard_strength} strong!" end def blurt # Not allowed to do this: private methods can't have an explicit receiver "My age is #{self.age}!" end end require 'irb'; IRB.start
然后你可以运行ruby dwarf.rb
并执行此操作:
gloin = Dwarf.new('Gloin', 253, 7) gimli = Dwarf.new('Gimli', 62, 9) gloin > gimli # false gimli > gloin # true gimli.name # 'Gimli' gimli.age # NoMethodError: private method `age' called for #<Dwarf:0x007ff552140128> gimli.beard_strength # NoMethodError: protected method `beard_strength' called for #<Dwarf:0x007ff552140128> gimli.greet # "Lo, I am Gimli, and have mined these 62 years.\ My beard is 9 strong!" gimli.blurt # private method `age' called for #<Dwarf:0x007ff552140128>
Ruby中的私有方法:
如果一个方法在Ruby中是私有的,那么它不能被明确的接收者(对象)调用。 它只能被隐式调用。 它可以被它所描述的类以及这个类的子类隐含地调用。
下面的例子会更好地说明这一点:
1)具有私有方法class_name的动物类
class Animal def intro_animal class_name end private def class_name "I am a #{self.class}" end end
在这种情况下:
n = Animal.new n.intro_animal #=>I am a Animal n.class_name #=>error: private method `class_name' called
2)动物的一个子类称为两栖动物:
class Amphibian < Animal def intro_amphibian class_name end end
在这种情况下:
n= Amphibian.new n.intro_amphibian #=>I am a Amphibian n.class_name #=>error: private method `class_name' called
正如你所看到的,私有方法只能被隐式地调用。 他们不能被明确的接收者调用。 出于同样的原因,私有方法不能在定义类的层次结构之外被调用。
Ruby中受保护的方法:
如果一个方法在Ruby中被保护,那么它可以被定义的类及其子类隐式地调用。 另外,只要接收者是自我的或与自我相同的类别,他们也可以被明确的接收者调用:
1)具有受保护方法protect_me的动物类
class Animal def animal_call protect_me end protected def protect_me p "protect_me called from #{self.class}" end end
在这种情况下:
n= Animal.new n.animal_call #=> protect_me called from Animal n.protect_me #=>error: protected method `protect_me' called
2)从动物类inheritance的哺乳类
class Mammal < Animal def mammal_call protect_me end end
在这种情况下
n= Mammal.new n.mammal_call #=> protect_me called from Mammal
3)从动物类inheritance的两栖动物类(与哺乳动物类相同)
class Amphibian < Animal def amphi_call Mammal.new.protect_me #Receiver same as self self.protect_me #Receiver is self end end
在这种情况下
n= Amphibian.new n.amphi_call #=> protect_me called from Mammal #=> protect_me called from Amphibian
4)一个叫Tree的类
class Tree def tree_call Mammal.new.protect_me #Receiver is not same as self end end
在这种情况下:
n= Tree.new n.tree_call #=>error: protected method `protect_me' called for #<Mammal:0x13410c0>
考虑一下Java中的私有方法。 当然,它可以从同一个类中调用,但也可以由同一个类的另一个实例调用:
public class Foo { private void myPrivateMethod() { //stuff } private void anotherMethod() { myPrivateMethod(); //calls on self, no explicit receiver Foo foo = new Foo(); foo.myPrivateMethod(); //this works } }
所以 – 如果调用者是同一个类的不同实例 – 我的私有方法实际上可以从“外部”访问,可以这么说。 这实际上使得它看起来并不全是私人的。
另一方面,在Ruby中,私有方法实际上仅仅是对当前实例的私有方法。 这是删除显式接收器提供的选项。
另一方面,我应该指出,在Ruby社区中,根本不使用这些可见性控件是很常见的,因为Ruby给了你方法来解决它们。 与Java世界不同的是,倾向于让所有的东西都可以访问,并相信其他的开发者不要把事情搞砸。
比较Java和Ruby的访问控制:如果方法在Java中声明为private,则只能由同一个类中的其他方法访问。 如果一个方法被声明为保护的,它可以被存在于同一个包中的其他类访问,也可以被另一个包中的类的子类访问。 当一个方法是公开的,每个人都可以看到。 在Java中,访问控制可见性概念取决于这些类在inheritance/包层次结构中的位置。
而在Ruby中,inheritance层次或者包/模块不适合。 所有关于哪个对象是方法的接收者。
对于Ruby中的私有方法,永远不能用明确的接收方来调用。 我们可以(只)用隐式接收者来调用私有方法。
这也意味着我们可以从它声明的类中调用私有方法,也可以调用此类的所有子类。
class Test1 def main_method method_private end private def method_private puts "Inside methodPrivate for #{self.class}" end end class Test2 < Test1 def main_method method_private end end Test1.new.main_method Test2.new.main_method Inside methodPrivate for Test1 Inside methodPrivate for Test2 class Test3 < Test1 def main_method self.method_private #We were trying to call a private method with an explicit receiver and if called in the same class with self would fail. end end Test1.new.main_method This will throw NoMethodError
您永远不能从定义它的类层次结构外部调用私有方法。
受保护的方法可以使用隐式接收方来调用,就像私有方法一样。 另外,如果接收者是“自己的”或“同一类别的对象”,被保护的方法也可以被明确的接收者调用(唯一的)。
class Test1 def main_method method_protected end protected def method_protected puts "InSide method_protected for #{self.class}" end end class Test2 < Test1 def main_method method_protected # called by implicit receiver end end class Test3 < Test1 def main_method self.method_protected # called by explicit receiver "an object of the same class" end end InSide method_protected for Test1 InSide method_protected for Test2 InSide method_protected for Test3 class Test4 < Test1 def main_method Test2.new.method_protected # "Test2.new is the same type of object as self" end end Test4.new.main_method class Test5 def main_method Test2.new.method_protected end end Test5.new.main_method This would fail as object Test5 is not subclass of Test1 Consider Public methods with maximum visibility
概要
公开:公共方法具有最大的可见性
受保护:受保护的方法可以使用隐式接收方来调用,就像私有方法一样。 另外,如果接收者是“自己的”或“同一类别的对象”,被保护的方法也可以被明确的接收者调用(唯一的)。
私有:对于Ruby中的私有方法,它永远不能用明确的接收方来调用。 我们可以(只)用隐式接收者来调用私有方法。 这也意味着我们可以从它声明的类中调用私有方法,也可以调用此类的所有子类。
在Ruby中,子类可以访问私有方法的部分原因是Ruby的inheritance与类是模块化的。在模块中,包含 – 在Ruby中,类实际上是一种提供inheritance的模块。
http://ruby-doc.org/core-2.0.0/Class.html
这意味着基本上是一个子类“包含”父类,以便有效地在子类中定义父类的函数, 包括私有函数 。
在其他编程语言中,调用方法涉及将方法名称冒泡到父类层次结构中,并find响应该方法的第一个父类。 相比之下,在Ruby中,虽然父类层次结构仍然存在,但父类的方法直接包含在已定义子类的方法列表中。