LoginSignup
528

More than 5 years have passed since last update.

Ruby の private と protected 。歴史と使い分け

Last updated at Posted at 2014-08-18

概要

Ruby の private と protected の歴史と使い分けについて説明します。

いろんなところで断片的に書かれていることなのですが、有益な情報を
一箇所に集めると自分の理解が深まりそう=>他の人にも役立つかな?
と思ったのでまとめてみました。

具体的には、伊藤淳一さんのブログ・るりま・Rubyメーリングリストの内容を
一箇所にまとめた上で、私が書いたサンプルコードを少し足した内容になっています。

ちなみに Java や C# 畑の人が Ruby の private / protected を使って驚いた、
的な情報が多いですが、私も例にもれず Java => Ruby 勢で驚いたパターンです。

Java の private と protected

  • public => どこからでもアクセス可能
  • protected => クラス内、同一パッケージ、サブクラスからアクセス可
  • private => クラス内のみアクセス可能

制限無く公開したいなら public 。
基本的に外部からは隠蔽するが、サブクラスやパッケージ内からのみ利用させたい場合は protected 。
基本的に外部からは隠蔽し、クラス内からのみ利用させたい場合は private 。

という使い分けになっていて、かなり分かりやすく、使い分ける目的も明確です。

今回の話には関係ありませんが、アクセス修飾子を省略した場合には default というスコープもあります。

  • default => クラス内、同一パッケージからアクセス可

Ruby の private と protected

るりまを確認

るりまでは下記のように説明されています。
http://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fdef.html#limit

  • public => public に設定されたメソッドは制限なしに呼び出せます。
  • protected => protected に設定されたメソッドは、そのメソッドを持つオブジェクトが self であるコンテキスト(メソッド定義式や instance_eval )でのみ呼び出せ ます。
  • private => private に設定されたメソッドは関数形式でしか呼び出せません。

※便宜上、るりまとは表記順を変えてあります。(スコープの範囲が広い順にした方が分かりやすいと思ったので)

説明だけだとピンと来ないかもしれないので、サンプルコードを提示します。

private と protected の動作を確認

require 'english'

class ScopeResearchClass
  def public_method
    'public'
  end

  def use_protected(other)
    puts other.protected_method
  end

  def use_private(other)
    other.private_method
  rescue
    puts $ERROR_INFO
  end

  def internal_use_private_and_protected
    puts protected_method
    puts private_method
  end

  def internal_use_private_and_protected_with_reciever
    puts self.protected_method
    self.private_method
  rescue
    puts $ERROR_INFO
  end

  protected

  def protected_method
    'protected'
  end

  private

  def private_method
    'private'
  end
end

pc1 = ScopeResearchClass.new
pc2 = ScopeResearchClass.new

begin
  pc1.protected_method
rescue
  # protected は外部からは呼び出せずにエラー
  puts $ERROR_INFO
end

begin
  pc1.private_method
rescue
  # private は外部からは呼び出せずにエラー
  puts $ERROR_INFO
end

# private / protected ともに内部から利用可能
pc1.internal_use_private_and_protected

# protected は レシーバーつきでも呼び出し可能
# private は レシーバーつきだと呼び出せず
pc1.internal_use_private_and_protected_with_reciever

class Hoge
  def can_not_use_external_protected_method(other)
    other.protected_method
  rescue
    puts $ERROR_INFO
  end
end

# 関係ないクラス内からは呼び出せないことを確認
Hoge.new.can_not_use_external_protected_method(pc2)

# protected メソッドは自クラスに別インスタンスを渡しても呼び出し可能
pc1.use_protected(pc2)

# private メソッドは自クラスに別インスタンスを渡した場合、レシーバーの指定が出来ないのでエラーになる
pc1.use_private(pc2)

class ChildProtectedClass < ScopeResearchClass
  def use_protected_from_child(other)
    puts other.protected_method
  end
end

# protected メソッドはサブクラスに別インスタンスを渡しても呼び出し可能
ChildProtectedClass.new.use_protected_from_child(pc2)
  • 出力
protected method `protected_method' called for #<ScopeResearchClass:0x0000060044c770>
private method `private_method' called for #<ScopeResearchClass:0x0000060044c770>
protected
private
protected
private method `private_method' called for #<ScopeResearchClass:0x0000060044c770>
protected method `protected_method' called for #<ScopeResearchClass:0x0000060044c748>
protected
private method `private_method' called for #<ScopeResearchClass:0x0000060044c748>
protected

protected を使うべき状況

protected を使うべき状況について、るりまにサンプルコードが掲載されていますが、
サンプルコードに少し補足をして、実行して動きを確認できるプログラムにしてみます。

class Foo
  def _val
    @val
  end
  protected :_val

  def op(other)

    # other も Foo のインスタンスを想定
    # _val が private だと関数形式でしか呼べないため
    # このように利用できない

    self._val + other._val
  end
end

f1 = Foo.new
f1.instance_variable_set(:@val, 1) # => @val に無理やり 1 を設定
f2 = Foo.new
f2.instance_variable_set(:@val, 2) # => @val に無理やり 2 を設定
puts f1.op(f2) # => 3

Ruby の private と protected の歴史

伊藤淳一さんの素晴らしいブログエントリに Ruby のパパ「 Matz 」さんとのやりとりが
まとまっています。
http://blog.jnito.com/entry/20120315/1331754912

  • Matz さん曰く
Rubyのprivateの発想の元になったのはSmalltalkの「privateカテゴリ」です。
使わないでね、というだけでアクセスできちゃう。
Rubyはそれよりは若干強制力があります。
Rubyの反C++・親Smalltalkの設計思想が垣間見えますね

後でprotectedを追加したのもまずかった。
これでC++とキーワードが同じでも意味がズレてることになってしまったので。

private と protected の使い分けに関する Matz さんの分かりやすい説明

Rubyのメーリングリストのやりとりより。
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/7669

  • Matz さん曰く
つまり,privateは自分からしか見えないメソッドであるのに対し
て,protectedは一般の人からは見られたくないが,仲間(クラスが
同じオブジェクト)からは見えるメソッドです.

protectedは例えば2項演算子の実装にもう一方のオブジェクトの状
態を知る必要があるか調べる必要があるが,そのメソッドをpublic
にして,広く公開するのは避けたいというような時に使います.

Ruby のメソッドの公開範囲の決定に関するマトリクス

スコープ 全体に公開したい 外部から隠蔽したい レシーバーを仲間が利用する
public ×
protected ×
private × ×

※仲間=自クラスかサブクラスのレシーバー

参照

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
528