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

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

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

但是,看起来 protectedprivate的作用是相同的,只是不能使用显式接收方调用 private方法(也就是说,self.protected_method工作,但是 self.private_method不工作)。

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

56099 次浏览

定义类或其子类的任何实例都可以调用 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

考虑 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 世界不同,现在的趋势是让所有东西都可以访问,并且相信其他开发人员不会把事情搞砸。

区别在于

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

Ruby 中的私有方法:

如果一个方法在 Ruby 中是私有的,那么它就不能被显式的接收者(对象)调用。它只能被隐式调用。它可以被描述它的类以及这个类的子类隐式地调用。

下面的例子将更好地说明这一点:

具有私有方法 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 中受到保护,那么定义类及其子类都可以隐式地调用它。此外,只要接收者是自我或者与自我属于同一类,它们也可以被显式接收者调用:

带有 protected 方法 protected _ me 的 Animal 类

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)一个叫做 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>

Ruby 中的子类之所以可以访问私有方法,部分原因是 Ruby 继承中的类是对 Module 的一层保护——在 Ruby 中,类实际上是一种提供继承的模块,等等。

Http://ruby-doc.org/core-2.0.0/class.html

这意味着基本上一个子类“包含”了父类,因此实际上父类的函数 包括私人活动也是在子类中定义的。

在其他编程语言中,调用方法涉及在父类层次结构中冒泡显示方法名,并查找响应该方法的第一个父类。相比之下,在 Ruby 中,虽然父类层次结构仍然存在,但是父类的方法直接包含在子类定义的方法列表中。

Java 与 Ruby 访问控制的比较: 如果方法在 Java 中被声明为 private,那么它只能被同一类中的其他方法访问。如果一个方法被声明为 protected,那么它可以被存在于同一个包中的其他类访问,也可以被不同包中的类的子类访问。当一个方法是公共的时候,它对每个人都是可见的。在 Java 中,访问控制可见性的概念取决于这些类在继承/包层次结构中的位置。

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

对于 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

永远不能从定义私有方法的类层次结构外部调用该私有方法。

可以使用隐式接收方调用受保护的方法,与 private 类似。此外,如果接收方是“ self”或“ an object of the same class”,则显式接收方(仅)也可以调用 protected 方法。

 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

摘要

公共方法: 公共方法具有最大的可见性

受保护的: 受保护的方法可以通过隐式接收方调用,就像 private 一样。此外,如果接收方是“ self”或“ an object of the same class”,则显式接收方(仅)也可以调用 protected 方法。

Private: 对于 Ruby 中的 Private 方法,永远不能用显式接收方调用它。我们只能使用隐式接收方调用私有方法。这也意味着我们可以从声明它的类以及该类的所有子类中调用私有方法。

前三种类型的访问说明符和定义其作用域的说明符。

  1. 公开-> 课堂以外的任何地方。
  2. Private-> 不能访问类外部。
  3. Protected-> 此方法不访问此方法定义的任何位置 范围。

但是我有一个解决这个问题的办法,对于所有的方法如何进行深入的解释。

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 = Test.new (“ test”)
  3. P test 姓名
  4. Add _ two (3)
  5. 列表项目
  6. 查看地址
  7. P r = Result.new (“”)
  8. New _ user

有什么区别吗?

解释私人方法

@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

我们可以问问 Freddie 事情进展如何,因为这是一个公开的方法。完全正确。这是正常的,也是可以接受的。

但是... 唯一知道 Freddie 内裤情况的人,就是 Freddie 自己。随便一个陌生人摸进弗雷迪的内裤试探一下情况是不行的不,不,这是非常非常隐私的我们不想把隐私暴露给外面的世界。

@freddie.how_are_your_underpants_feeling?
# => # NoMethodError: private method `how_are_your_underpants_feeling?' called

保护方法解释

想想这个:

class Person
    

protected


def gimme_your_credit_card! # protected method
puts "Fine. 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.gimme_your_credit_card!
end
end


@husband = Person.new
@mrs = Wife.new
@mrs.i_am_buying_another_handbag_with_your_card(@husband)
# => puts "Fine. Whatever. Here it is: 1234-4567-8910"

我们可以接受 mrs获取我们的信用卡详细信息,因为 mrs是我们的血肉之躯,是从 Person 继承的,但我们不希望随机的个人获取我们的信用卡详细信息。

如果我们试图在子类之外这样做,它将失败:

@mrs = Wife.new
@mrs.gimme_your_credit_card!
# => protected method gimme_your_credit_card! called for #<Wife:0x00005567b5865818> (NoMethodError)

摘要

  • 只能从内部调用 private 方法,而且不能有“显式接收方”。(严格地说,您可以使用一点 Ruby 魔法来访问私有方法,但是我暂时将忽略它)。
  • 可以在子类中调用 protected 方法。
  • 我使用例子/类比来帮助你清楚地记住。