Edited at

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

More than 3 years have passed since last update.


概要

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
×

×

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


参照