web-dev-qa-db-ja.com

なぜRubyにはprivateメソッドとprotectedメソッドの両方があるのですか?

この記事 を読む前に、Ruby=

  • public-任意のオブジェクトからアクセスできます(例:Obj.new.public_method
  • protected-サブクラスと同様に、オブジェクト自体からのみアクセスできます
  • private-protectedと同じですが、メソッドはサブクラスに存在しません

ただし、protectedprivateは同じ動作をするようです。ただし、明示的なレシーバーではprivateメソッドを呼び出せない(つまり、self.protected_methodは機能しますが、 self.private_methodは含まれません)。

これのポイントは何ですか?明示的なレシーバーでメソッドを呼び出したくないシナリオはいつありますか?

132
Kyle Slattery

protectedメソッドは、定義クラスまたはそのサブクラスの任意のインスタンスによって呼び出すことができます。

privateメソッドは、呼び出し元オブジェクト内からのみ呼び出すことができます。別のインスタンスのプライベートメソッドに直接アクセスすることはできません。

簡単で実用的な例を次に示します。

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

some_methodはここではprivateにはできません。明示的なレシーバーをサポートするために必要なため、protectedでなければなりません。通常、内部ヘルパーメソッドは、このように呼び出す必要がないため、通常privateになります。

これは、JavaまたはC++が機能する方法とは異なることに注意することが重要です。private in Rubyは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
150
dbyrne

違い

  • 誰でもパブリックメソッドを呼び出すことができます。
  • 保護されたメソッドを呼び出すことができますまたはクラス(または子孫クラス)の別のメンバーは、外部から保護されたメソッドを呼び出すことができます。誰もできません。
  • プライベートメソッドは、selfの暗黙のレシーバでのみ呼び出すことができるため、自分でしか呼び出すことができません。 Even youでもself.some_private_methodを呼び出すことはできません。 selfを暗黙に指定してprivate_methodを呼び出す必要があります。 ( iGEL 指摘:「ただし、例外が1つあります。プライベートメソッド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

次に、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>
77
Nathan Long

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)protectedメソッドprotect_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>
45
Aaditi Jain

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の世界とは異なり、すべてをアクセス可能にし、他の開発者が物事を台無しにしないことを信頼する傾向があります。

7
Jacob Mattison

Java Rubyに対するアクセス制御の比較:メソッドがJavaでprivateと宣言されている場合、同じクラス内の他のメソッドからのみアクセスできます。メソッドが同じパッケージ内に存在する他のクラス、および異なるパッケージ内のクラスのサブクラスからアクセスできます。メソッドがパブリックの場合、誰でも見ることができます。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のように、暗黙のレシーバーで保護されたメソッドを呼び出すことができます。さらに、レシーバーが「自己」または「同じクラスのオブジェクト」である場合、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

概要

Public:パブリックメソッドは最大の可視性を持ちます

Protected:プライベートのように、保護されたメソッドは暗黙的なレシーバーで呼び出すことができます。さらに、受信者が「自己」または「同じクラスのオブジェクト」である場合、保護されたメソッドを明示的な受信者(のみ)から呼び出すこともできます。

プライベート:Rubyのプライベートメソッドの場合、明示的なレシーバで呼び出すことはできません。暗黙のレシーバーでプライベートメソッドを呼び出すことができます(のみ)。これは、このクラスのすべてのサブクラスと同様に、宣言されたクラス内からプライベートメソッドを呼び出すことができることも意味します。

2
Neha Chopra

Rubyのサブクラスがプライベートメソッドにアクセスできる理由の一部は、クラスのRuby継承がモジュールを含む薄いシュガーコーティングであるためです-Rubyでは、クラスは実際には継承などを提供するモジュールの種類.

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

これが意味することは、基本的にサブクラスが親クラスを「インクルード」するため、親クラスの関数がプライベート関数を含むように、サブクラスでまあ。

他のプログラミング言語では、メソッドを呼び出すには、メソッド名を親クラス階層にバブリングし、メソッドに応答する最初の親クラスを見つける必要があります。対照的に、Rubyでは、親クラスの階層はまだ存在していますが、親クラスのメソッドは、サブクラスが定義したメソッドのリストに直接含まれています。

1
madumlao