Kyl*_*ery 134 ruby language-design access-specifier
在我阅读本文之前,我认为Ruby中的访问控制工作方式如下:
public
- 可以被任何对象访问(例如Obj.new.public_method
)protected
- 只能从对象本身以及任何子类中访问private
- 与protected相同,但子类中不存在该方法但是,看起来protected
并且private
行为相同,除了你不能private
用显式接收器调用方法(即self.protected_method
工作,但self.private_method
没有).
这有什么意义?什么时候你不希望你的方法用显式接收器调用?
dby*_*rne 153
protected
方法可以由定义类或其子类的任何实例调用.
private
只能从调用对象中调用方法.您无法直接访问其他实例的私有方法.
这是一个快速的实际例子:
def compare_to(x)
self.some_method <=> x.some_method
end
Run Code Online (Sandbox Code Playgroud)
some_method
不能在private
这里.一定是protected
因为你需要它来支持显式接收器.您通常可以使用典型的内部帮助程序方法,private
因为它们永远不需要像这样调用.
值得注意的是,这与Java或C++的工作方式不同.private
在Ruby中类似于protected
Java/C++,因为子类可以访问该方法.在Ruby中,没有办法像private
在Java中那样限制从子类访问方法.
无论如何,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
Run Code Online (Sandbox Code Playgroud)
Nat*_*ong 77
self
.即使你不能打电话self.some_private_method
; 你必须调用private_method
与self
暗示.(iGEL指出:"但是有一个例外.如果你有一个私有方法age =,你可以(并且必须)用self调用它来将它与局部变量分开.")在Ruby中,这些区别只是从一个程序员到另一个程序员的建议.非公开方法是一种说法"我保留改变这一点的权利;不依赖它".但你仍然可以使用锋利的剪刀,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
Run Code Online (Sandbox Code Playgroud)
然后你可以运行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>
Run Code Online (Sandbox Code Playgroud)
Aad*_*ain 48
如果一个方法在Ruby中是私有的,那么它不能被显式接收器(对象)调用.它只能被隐式调用.它可以由描述它的类以及该类的子类隐式调用.
以下示例将更好地说明:
1)具有私有方法class_name的Animal类
class Animal
def intro_animal
class_name
end
private
def class_name
"I am a #{self.class}"
end
end
Run Code Online (Sandbox Code Playgroud)
在这种情况下:
n = Animal.new
n.intro_animal #=>I am a Animal
n.class_name #=>error: private method `class_name' called
Run Code Online (Sandbox Code Playgroud)
2)动物的一个子类叫做两栖动物:
class Amphibian < Animal
def intro_amphibian
class_name
end
end
Run Code Online (Sandbox Code Playgroud)
在这种情况下:
n= Amphibian.new
n.intro_amphibian #=>I am a Amphibian
n.class_name #=>error: private method `class_name' called
Run Code Online (Sandbox Code Playgroud)
如您所见,私有方法只能隐式调用.它们不能被显式接收器调用.出于同样的原因,不能在定义类的层次结构之外调用私有方法.
如果一个方法在Ruby中受到保护,那么它可以由定义类及其子类隐式调用.此外,只要接收者是自己的或与自己相同的类,它们也可以由显式接收者调用:
1)具有受保护方法protect_me的Animal类
class Animal
def animal_call
protect_me
end
protected
def protect_me
p "protect_me called from #{self.class}"
end
end
Run Code Online (Sandbox Code Playgroud)
在这种情况下:
n= Animal.new
n.animal_call #=> protect_me called from Animal
n.protect_me #=>error: protected method `protect_me' called
Run Code Online (Sandbox Code Playgroud)
2)哺乳动物类,遗传自动物类
class Mammal < Animal
def mammal_call
protect_me
end
end
Run Code Online (Sandbox Code Playgroud)
在这种情况下
n= Mammal.new
n.mammal_call #=> protect_me called from Mammal
Run Code Online (Sandbox Code Playgroud)
3)从动物类继承的两栖类(与哺乳动物类相同)
class Amphibian < Animal
def amphi_call
Mammal.new.protect_me #Receiver same as self
self.protect_me #Receiver is self
end
end
Run Code Online (Sandbox Code Playgroud)
在这种情况下
n= Amphibian.new
n.amphi_call #=> protect_me called from Mammal
#=> protect_me called from Amphibian
Run Code Online (Sandbox Code Playgroud)
4)一个叫做Tree的类
class Tree
def tree_call
Mammal.new.protect_me #Receiver is not same as self
end
end
Run Code Online (Sandbox Code Playgroud)
在这种情况下:
n= Tree.new
n.tree_call #=>error: protected method `protect_me' called for #<Mammal:0x13410c0>
Run Code Online (Sandbox Code Playgroud)
考虑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
}
}
Run Code Online (Sandbox Code Playgroud)
所以 - 如果调用者是我同一个类的不同实例 - 我的私有方法实际上可以从"外部"访问,可以这么说.这实际上使它看起来不是那么私密.
另一方面,在Ruby中,私有方法实际上只对当前实例是私有的.这是删除显式接收器提供的选项的原因.
另一方面,我当然应该指出,在Ruby社区中根本不使用这些可见性控件是非常常见的,因为Ruby无论如何都会为你提供绕过它们的方法.与Java世界不同,趋势是让所有开发人员都可以访问并信任其他开发人员,而不是搞砸了.
@freddie = Person.new
@freddie.hows_it_going?
# => "oh dear, i'm in great pain!"
class Person
# public method
def hows_it_going?
how_are_your_underpants_feeling?
end
private
def how_are_your_underpants_feeling? # private method
puts "oh dear, i'm in great pain!"
end
end
Run Code Online (Sandbox Code Playgroud)
鉴于这是一个公共方法,我们可以询问 Freddie 事情进展如何。这是完全正确的。这很正常并且可以接受。
但是……能够知道弗雷迪内裤情况如何的人,只有弗雷迪本人。对于随机的陌生人来说,把手伸进弗雷迪的内裤是不行的——不,不——这是非常非常私密的事情,我们不想把私密的东西暴露给外界。换句话说,我们可能不想向世界上的任何调用者公开可变数据。有人可能会改变一个值,而我们会陷入一个痛苦的世界,试图找出错误的来源。
@freddie.how_are_your_underpants_feeling?
# => # NoMethodError: private method `how_are_your_underpants_feeling?' called
Run Code Online (Sandbox Code Playgroud)
考虑一下:
class Person
protected
def hand_over_the_credit_card! # protected method
puts "Lawd have mercy. Whatever. Here it is: 1234-4567-8910"
end
end
class Rib < Person
end
class Wife < Rib # wife inherits from Rib
def i_am_buying_another_handbag_with_your_card(husband)
husband.hand_over_the_credit_card! # equalityInAction
end
end
@husband = Person.new
@mrs = Wife.new
@mrs.i_am_buying_another_handbag_with_your_card(@husband)
# => puts "Lawd have mercy. Whatever. Here it is: 1234-4567-8910"
Run Code Online (Sandbox Code Playgroud)
我们可以mrs
接受我们的信用卡详细信息,因为mrs
我们的血肉是从 Person 继承的,所以她可以把它吹到一些鞋子等上,但我们不希望随机的个人访问我们的信用卡详细信息。
如果我们尝试在子类之外执行此操作,则会失败:
@mrs = Wife.new
@mrs.hand_over_the_credit_card!
# => protected method hand_over_the_credit_card! called for #<Wife:0x00005567b5865818> (NoMethodError)
Run Code Online (Sandbox Code Playgroud)