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


141

在阅读本文之前,我认为Ruby中的访问控制是这样工作的:

  • public-可以被任何对象(例如Obj.new.public_method)访问
  • protected -只能从对象本身以及任何子类内部访问
  • private -与protected相同,但是该方法在子类中不存在

但是,除了您不能使用显式接收器调用方法(即有效,但不能)之外,看起来protectedprivate动作相同。privateself.protected_methodself.private_method

这有什么意义呢?在什么情况下,您不希望使用显式接收器调用方法?


3
如果Object允许所有实例调用其他实例的私有方法Object,那么可能会说诸如此类5.puts("hello world")
sepp2k 2010年

Answers:


161

protected 定义类或其子类的任何实例都可以调用这些方法。

private方法只能在调用对象内调用。您不能直接访问另一个实例的私有方法。

这是一个快速的实际示例:

def compare_to(x)
 self.some_method <=> x.some_method
end

some_method不能在private这里。一定是protected因为您需要它来支持显式接收器。典型的内部辅助方法通常是这样,private因为它们不需要像这样被调用。

重要的是要注意,这与Java或C ++的工作方式不同。privateRuby protected中的Java 与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

9
嗯,那很有意义。我的误解来自于思考privateprotected不得不做一个子类是否可以继承一个方法,但是实际上是关于可以从哪里调用该方法。谢谢!
Kyle Slattery 2010年

3
同样,在生成文档时,RDoc默认会忽略私有方法,而不会生成受保护方法。您始终可以使用--all标志来包括它们。
jasoares 2014年

但是,如果您真的希望将其私有化,您可以覆盖send吗?
Cyoce

78

区别

  • 任何人都可以调用您的公共方法。
  • 您可以调用受保护的方法,或者您的类的另一个成员(或子孙类)可以从外部调用受保护的方法。没有人可以。
  • 只有您可以调用自己的私有方法,因为只能使用的隐式接收器来调用它们self即使你不能打电话self.some_private_method; 你必须调用private_methodself暗示。
    • iGEL指出:“但是,有一个例外。如果您有私有方法age =,则可以(必须)使用self进行调用,以将其与局部变量分开。”
    • 由于Ruby 2.7self接收器可以是显式的,因此self.some_private_method是允许的。(即使运行时值与相同,仍然不允许任何其他显式接收器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

然后,您可以运行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>

8
很好的解释!但是有一个例外。如果您有一个私有方法age=,则可以(必须)调用它self以将其与局部变量分开。
iGEL 2013年

如果您将“问候”设置为受保护的方法,那么为什么不能执行gimli.greet?由于gimli是Dwarf类的成员,因此它应该能够在不引起骚扰的情况下调用此方法吗?
JoeyC

@JoeyC因为当您这样做时gimli.greetgimli不是调用方,而是接收方。调用方是“顶级执行环境”,它实际上是的临时实例Object。试试这个:ruby -e 'p self; p self.class'
Kelvin

52

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

在这种情况下:

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中受保护,那么定义类及其子类都可以隐式调用该方法。另外,只要接收者是self或与self属于同一类,它们也可以由显式接收者调用:

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)从动物类继承而来的哺乳动物类

class Mammal < Animal
  def mammal_call
    protect_me
  end
end 

在这种情况下

n= Mammal.new
n.mammal_call #=> protect_me called from Mammal

3)从动物类继承的两栖类(与哺乳动物类相同)

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)称为树的类

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>

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
   }
}

因此,可以说,如果调用者是同一类的不同实例,那么实际上可以从“外部”访问我的私有方法。实际上,这似乎使它看起来不是那么私密。

另一方面,在Ruby中,私有方法确实意味着仅对当前实例私有。这就是删除显式接收器选项所提供的。

另一方面,我当然应该指出,在Ruby社区中,根本不使用这些可见性控件是很普遍的,因为Ruby提供了无论如何都可以绕开它们的方法。与Java世界不同,趋势是使所有内容都可以访问并信任其他开发人员不要搞砸。


9
“在Ruby社区中完全不使用这些可见性控件是很普遍的”-可能是正确的,但我想我们应该使用它们。像常量一样,它们不是手铐,而是一个程序员与另一位程序员的交流:“我建议您不要再说了。” 您可以依靠我的公开方法;我可能会更改我的私有方法而不会发出警告,因为我会考虑它们的实现细节。
内森·朗

另一方面,在Ruby中,私有方法确实意味着仅对当前实例私有。“ 这不是真的。您仍然可以不小心覆盖父类的私有方法(有些类甚至将其列为API的一部分)。
富兰克林于

1
@FranklinYu那与他写的内容无关。Ruby中的隐私权是关于对象而不是类的,它是关于调用方法而不是定义它们的。私有方法只能由同一对象的另一个方法调用;它没有什么做什么类的方法进行定义。
philomory

2

私有方法可以被Ruby的子类访问的部分原因是,具有类的Ruby继承是对Module的精打细算-在Ruby中,类实际上是提供继承的一种模块,等等。

http://ruby-doc.org/core-2.0.0/Class.html

这意味着基本上子类会“包含”父类,以便在子类中也有效地定义父类的函数(包括私有函数)

在其他编程语言中,调用方法涉及将方法名称冒泡到父类层次结构中,并找到响应该方法的第一个父类。相反,在Ruby中,当父类层次结构仍然存在时,父类的方法直接包含在已定义的子类的方法列表中。


2

Java与Ruby的访问控制的比较:如果方法在Java中声明为私有,则该方法只能由同一类中的其他方法访问。如果方法被声明为受保护的,则可以由同一包中存在的其他类以及其他包中的该类的子类访问该方法。当一种方法公开时,每个人都可以看到。在Java中,访问控制可见性概念取决于这些类在继承/包层次结构中的位置。

而在Ruby中,继承层次结构或包/模块不适合。关于哪个对象是方法的接收者。

对于Ruby中的私有方法,永远不能使用显式接收器来调用它。我们(只能)使用隐式接收器调用private方法。

这也意味着我们可以从在其声明的类以及该类的所有子类中调用私有方法。

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

您永远不能从定义了它的类层次结构之外调用私有方法。

可以使用隐式接收器来调用受保护的方法,就像private一样。此外,如果接收者是“自身”或“同一类的对象”,则显式接收者也可以(仅)调用受保护的方法。

 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中的私有方法,永远不能使用显式接收器调用它。我们(只能)使用隐式接收器调用private方法。这也意味着我们可以从在其声明的类以及该类的所有子类中调用私有方法。


0
First Three types of access specifiers and those define thier scope.
1.Public    ->  Access anywhere out side the class.
2.Private   ->  Can not access outside the class. 
3.Protected ->  This Method not access anywhere this method define 
                scope.

But i have a solution for this problem for all method how to access explain in depth. 

class Test
attr_reader :name
def initialize(name)
  @name = name
end

def add_two(number)
  @number = number 
end

def view_address
  address("Anyaddress")
end

private 
def address(add)
   @add = add
end

protected 
def user_name(name)
  # p 'call method'
  @name = name
end
end

class Result < Test
def new_user
  user_name("test355")
end
end
  1. 对象清单
  2. p测试= Test.new(“测试”)
  3. p test.name
  4. p test.add_two(3)
  5. 项目清单
  6. p test.view_address
  7. pr = Result.new(“”)
  8. p r.new_user

编辑代码时出现问题。第二课在上一篇文章中显示。现在我解释如何访问所有方法。首先创建Test类对象。但是私有方法无法访问外部类,然后访问私有方法。我们通过主对象创建view_address方法访问。并且还保护方法访问创建继承。
hardik
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.