Rubyでのクラスのアクセス制御

Rubyは,JavaやC++と同様に,クラスの利用者がクラスの内部の実装に必要以上に依存しないように,アクセス制御を行うことができます。

クラスのメンバ変数へのアクセスは,同名のメソッドによって行われるので,メソッドのアクセスをコントロールすれば十分です。

Rubyでは,Module#public, Module#protected,Module#privateというメソッドを用いて(予約語ではありません),アクセス制御を行います。これらのメソッドを引数なしで呼び出すと,現在のスコープのデフォルト・アクセス可能性(accessibility)を変更し,以降のメソッド定義は,そのアクセス可能性で行われます。メソッド名のシンボルを引数にして呼び出すと,そのメソッドのアクセス可能性を変更します。

メソッドのアクセス可能性は動的に変更でき,アクセス可能かどうかの判定は実行時に行われます。すなわち,実際にメソッド呼び出しを行うときに,アクセス可能かどうか判定されます。

publicメソッド,protectedメソッド,privateメソッドの呼び出しは,次のような制限があります。

publicメソッド
何の制限もなく,どこからでも呼び出せます。
protectedメソッド
そのメソッドが定義されたクラスのメソッドからか,そのサブクラスのメソッドからしか呼び出せません。
privateメソッド
明示的にレシーバを指定して呼び出すことができません。したがって,privateメソッドが定義されたクラスのメソッドか,そのサブクラスのメソッドからしか呼び出せず,さらに,異なるインスタンスを対象にして呼び出すことはできません。

protectedメソッドの例です。サブクラスではないクラスから呼び出そうとするとNameErrorが発生します。

  2| class B
  3|   protected
  4|   def foo(x)
  5|     p x
  6|   end
  7| end
  8| 
  9| class D < B
 10|   def bar(x)
 11|     foo(x * 2)
 12|   end
 13| end
 14| 
 15| class O
 16|   def baz(x)
 17|     B.new.foo(x * 3) #=> NameError
 18|   end
 19| end
 20| D.new.bar(1) #=> 2
 21| O.new.baz(2)

privateメソッドの例です。同じクラス内でも別のインスタンスであればエラーになります。

  1| class Foo
  2|   def c1
  3|     pri
  4|   end
  5| 
  6|   def c2(o)
  7|     o.pri  #=> NameError
  8|   end
  9|   
 10|   private
 11|   def pri
 12|     p true
 13|   end
 14| end
 15| 
 16| obj = Foo.new
 17| obj.c1        #=> true
 18| obj.c2(Foo.new)

Module#public, Module#protected,Module#privateによって陽にメソッドのアクセス可能性を変更したり,スコープのデフォルト・アクセス可能性を変更しない場合,クラス定義の中でのデフォルト・アクセス可能性はpublicです。ただし,例外的にinitializeメソッドは,常にprivateになります。

クラス定義の外でメソッドを定義するとき(関数形式,Kernelモジュールのメソッドになる),デフォルトのアクセス可能性はprivateです。これがどこからでも呼べるのは,ObjectクラスがKernelモジュールをincludeしており,クラスはすべてObjectのサブクラスだからです。