为什么Ruby有私有和受保护的方法?

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中类似于protectedJava/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)

  • 啊,好吧,这更有意义.我的误解来自于思考`private` vs`protected`必须要做的事情是子类是否可以继承一个方法,但它实际上是关于可以调用方法的地方.谢谢! (9认同)
  • 默认情况下,RDoc在生成文档时会忽略私有方法,而受保护的文档则不会.您始终可以使用--all标志来包含它们. (3认同)

Nat*_*ong 77

区别

  • 任何人都可以调用您的公共方法
  • 您可以调用受保护的方法,或者您的类的其他成员(或后代类)可以从外部调用受保护的方法.没人能.
  • 只有你可以调用你的私有方法,因为它们只能用隐式接收器调用self.即使你不能打电话self.some_private_method; 你必须调用private_methodself暗示.(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)

  • 很好的解释!但是有一个例外.如果你有一个私有方法`age =`,你可以(并且必须)用`self`来调用它来将它与局部变量分开. (8认同)

Aad*_*ain 48

Ruby中的私有方法:

如果一个方法在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中的受保护方法:

如果一个方法在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)


Jac*_*son 7

考虑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世界不同,趋势是让所有开发人员都可以访问并信任其他开发人员,而不是搞砸了.

  • "在Ruby社区中,根本不使用这些可见性控制是很常见的" - 这可能是真的,但我会说我们应该使用它们.像常数一样,它们不是手铐,而是从一个程序员到另一个程序员的沟通:"我建议你不要再这样了." 你可以依靠我的公共方法; 我可以在没有警告的情况下更改我的私有方法,因为我认为它们是实现细 (8认同)

BKS*_*eon 5

有什么不同?

私有方法解释

@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)

概括

  • 私有方法只能从内部调用,并且没有“显式接收者”。(严格来说,您可以使用一点 ruby​​ 魔法来访问私有方法,但我暂时忽略它)。
  • 受保护的方法可以在子类中调用。
  • 我用例子/类比来帮助你生动地记住。