Rubyにプライベートメソッドと保護メソッドの両方があるのはなぜですか?


141

この記事を読む前に、Rubyのアクセス制御は次のように機能すると思っていました。

  • public-任意のオブジェクトによってアクセスすることができる(例えばObj.new.public_method
  • protected -オブジェクト自体およびすべてのサブクラス内からのみアクセスできます
  • private -protectedと同じですが、メソッドはサブクラスに存在しません

しかし、ように見えるprotectedし、privateあなたが呼び出すことができないという事実を除いて、同じように動作するprivate(つまり、明示的なレシーバを持つメソッドをself.protected_method動作しますが、self.private_methodしません)。

これの意味は何ですか?メソッドが明示的なレシーバーで呼び出されないようにするシナリオはいつありますか?


3
のすべてのインスタンスがのObject他のすべてのインスタンスのプライベートメソッドの呼び出しを許可されている場合、のObjectようなことを言うことは可能5.puts("hello world")です。
sepp2k 2010

回答:


161

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

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

ここに簡単な実用的な例があります:

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

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

これは、JavaまたはC ++の動作方法とは異なることに注意することが重要です。privateRuby 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

9
ああ、わかりました。私の誤解が考えから来たprivateprotectedサブクラスがメソッドを継承することができるかどうかをしなければならなかったが、それはメソッドから呼び出すことができる場所について、実際のです。ありがとう!
カイルスラッテリー2010

3
また、プライベートメソッドは、ドキュメントを生成するときにRDocによってデフォルトで無視されますが、保護されたメソッドは無視されません。それらを含めるには、常に--allフラグを使用できます。
jasoares 2014年

しかし、本当にプライベートにしたい場合は、オーバーライドできませんsendか?
Cyoce 2017年

78

違い

  • 誰でもあなたのパブリックメソッドを呼び出すことができます。
  • あなたは保護されたメソッドを呼び出すことができます、またはあなたのクラスの別のメンバー(または子孫クラス)は、外部からあなたの保護されたメソッドを呼び出すことができます。他の誰もできません。
  • プライベートメソッドを呼び出すことができるのは、暗黙的なレシーバのでのみ呼び出せるためですselfあなたも電話することはできませんself.some_private_method。あなたが呼び出す必要がありますprivate_methodself暗示します。
    • iGELは次のように指摘しています。「ただし、例外が1つあります。プライベートメソッドage =がある場合、それをselfで呼び出してローカル変数から分離することができます(そしてする必要があります)。
    • Ruby 2.7以降は、selfレシーバーを明示的にすることができるため、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
いい説明だ!ただし、例外が1つあります。プライベートメソッドがある場合は、ローカル変数から分離するためにage=それを呼び出すことができます(また、呼び出す必要があります)self
iGEL 2013年

"greet"を保護されたメソッドにした場合、なぜgimli.greetを実行できないのですか?gimliはDwarfクラスのメンバーなので、嫌がらせなしにこのメソッドを呼び出すことができませんか?
JoeyC 2015年

@JoeyCを実行gimli.greetするのgimliは、発信者ではなく受信者だからです。呼び出し元は「トップレベルの実行環境」であり、実際にはのアドホックインスタンスですObject。これを試してください:ruby -e 'p self; p self.class'
ケルビン

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でメソッドが保護されている場合は、定義クラスとそのサブクラスの両方から暗黙的に呼び出すことができます。さらに、レシーバーが自分自身または自分と同じクラスである限り、明示的なレシーバーから呼び出すこともできます。

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

この場合:

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>

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コミュニティでは、これらの可視性コントロールをまったく使用しないことはかなり一般的です」-これは本当かもしれませんが、私たちはそれらを使用するべきだと思います。定数のように、それらは手錠ではなく、あるプログラマーから別のプログラマーへのコミュニケーションです:「これをそのままにしておくことをお勧めします。」私のパブリックメソッドに依存できます。プライベートメソッドは実装の詳細を考慮しているため、警告なしに変更する場合があります。
Nathan Long

一方、Rubyでは、プライベートメソッドは実際には現在のインスタンスに対してのみプライベートであることを意味します。" 本当じゃない。親クラスのプライベートメソッドを誤って上書きする可能性があります(一部のクラスでは、これをAPIの一部としてリストしています)。
フランクリンユー

1
@FranklinYuそれは彼が書いたものには関係ありません。Rubyのプライバシーは、クラスではなくオブジェクトに関するものであり、メソッドの定義ではなくメソッドの呼び出しに関するものです。プライベートメソッドは、同じオブジェクトの別のメソッドによってのみ呼び出すことができます。それは、メソッドが定義されたもので、クラスとは何の関係もありません。
philomory

2

Rubyのサブクラスがプライベートメソッドにアクセスできる理由の1つは、クラスを使用したRubyの継承がモジュールインクルードを薄く覆っているからです。Rubyでは、実際、クラスは継承を提供する一種のモジュールなどです。

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

つまり、基本的にサブクラスは親クラスを「インクルード」し、親クラスの関数(プライベート関数を含む)がサブクラスでも効果的に定義されるようにします。

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


2

Rubyに対するJavaのアクセス制御の比較:メソッドがJavaでプライベートとして宣言されている場合、同じクラス内の他のメソッドからのみアクセスできます。メソッドが保護されていると宣言されている場合、同じパッケージ内に存在する他のクラスや、別のパッケージ内のクラスのサブクラスからアクセスできます。メソッドが公開されると、誰でも見ることができます。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

定義されたクラス階層の外部からプライベートメソッドを呼び出すことはできません。

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

 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

概要

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

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

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


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 = Test.new( "test")
  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

コードの編集におけるいくつかの問題。前の投稿の2行目が1行で表示されます。すべてのメソッドにアクセスする方法を説明します。最初にテストクラスオブジェクトを作成します。ただし、プライベートメソッドは外部クラスにアクセスできず、プライベートメソッドにアクセスできません。メインオブジェクトを介してview_addressメソッドアクセスを作成します。保護されたメソッドへのアクセスも継承を作成します。
hardik
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.