【Ruby v2.6】クラスメソッド その3(public, protected, private)
前置き
Ruby のクラスメソッドについてまとめます。 その3は、クラスメソッドの public, protected, private について
書き方
クラス定義内で、デフォルトでクラスメソッドを定義すると、public のクラスメソッドとなります。
ただ、インスタンスメソッドと同じように、クラス定義内に private
、 protected
キーワードの後にクラスメソッドの定義を記述しても、プライベート、パブリックなクラスメソッドにはなりません。
書き方は、2通りあります。
public_class_method
、private_class_method
を使って設定する。(但し、protected_class_method
がないので、protected なクラスメソッドはこの方法では作れません。。。)クラスメソッドは、特異クラス内でのインスタンスメソッドなので、特異クラスをオープンし、そこで
private
、protected
キーワードで設定する。
private_class_method
を使う場合
private_class_method
を使う場合は、先にクラスメソッドを定義した後で、private_class_method :[メソッド名]
の形式で記述します。
class A # self をつけて定義すると、全て public なクラスメソッドになる。 def self.public_method p "called self.public_method." end def self.private_method p "called self.private_method." end # private_class_method を使って、プライベート化する。 private_class_method :private_method # 各メソッドを呼び出すためのクラスメソッド def self.call_public_method A.public_method end def self.call_private_method private_method # レシーバーはつけてはいけない end end # クラス A を継承するクラス B class B < A # 継承されたクラス B から、クラス A のメソッドを呼び出してみる。 def self.call_public_method A.public_method end def self.call_private_method A.private_method end # クラス B のインスタンスから呼び出せるか? def call_public_method A.public_method end def call_private_method A.private_method end end
動作を確認してみます。
A.methods(false) # => [:public_method, :call_public_method, :call_private_method] A.private_methods(false) # => [:private_method, :inherited, :initialize]
確かに、クラスメソッド private_method
がプライベートと認識されています。
続いて、クラスA, クラスB のクラスメソッドから呼び出してみます。
A.call_public_method # => "cataed self.public_method." A.call_private_method # => "called self.private_method." B.call_public_method # => "called self.public_method." B.call_private_method # => NoMethodError (private method `private_method' called for A:Class)
クラス A からはプライベートメソッドが呼び出せて、クラス B からはクラス A のプライベートメソッドは呼び出せていません。さらに、B のインスタンスから呼び出せるか、を確認します。
obj = B.new obj.call_public_method # => "called self.public_method." obj.call_private_method # => NoMethodError (private method `private_method' called for A:Class)
クラス B のインスタンスからも、クラス A のプライベートメソッドの呼び出しができなくなっています。
特異クラスを開いて定義する場合
特異クラスをオープンし、特異クラスのインスタンスメソッドとしてクラスメソッドを定義する方法です。この場合は、インスタンスメソッドの定義方法と同じように、 protected
、private
の後にメソッド定義をすることで、protected, private の定義ができます。
class A # クラス A の特異クラスをオープンする。 class << self def public_method p "called public_method." end protected # ここ以降は protected なメソッド def protected_method p "called protected_method." end private # ここ以降は private なメソッド def private_method p "called private_method" end end # 特異クラスはここで終わり # 各メソッドを呼び出すためのクラスメソッド def self.call_public_method A.public_method end def self.call_protected_method A.protected_method end def self.call_private_method private_method # レシーバーはつけてはいけない end # クラス A のインスタンスから呼び出せるか? def call_public_method A.public_method end def call_protected_method A.protected_method end def call_private_method A.private_method end end # クラス A を継承するクラス B class B < A # 継承されたクラス B から、クラス A のメソッドを呼び出してみる。 def self.call_public_method A.public_method end def self.call_protected_method A.protected_method end def self.call_private_method A.private_method end # クラス B のインスタンスから呼び出せるか? def call_public_method A.public_method end def call_protected_method A.protected_method end def call_private_method A.private_method end end
同じように確認してみます。まず、メソッド一覧を確認します。
A.methods(false) # => [:call_private_method, :public_method, :protected_method, :call_public_method, :call_protected_method] A.private_methods(false) # => [:private_method, :inherited, :initialize] A.protected_methods(false) # => [:protected_method]
private_method
、protected_method
が、ちゃんと認識されています。
続けて、クラスメソッドからの呼び出しです。
A.call_public_method # => "called public_method." A.call_protected_method # => "called protected_method." A.call_private_method # => "called private_method" B.call_public_method # => "called public_method." B.call_protected_method # => "called protected_method." B.call_private_method # => NoMethodError (private method `private_method' called for A:Class)
クラス A を継承しているクラス B からは、protected は呼び出せていますが、private は呼び出せません。 さらに、クラス A と無関係なクラス C から、クラス A のprotected_method が呼び出せるか?
class C def self.call_protected_method A.protected_method end end C.call_protected_method # => NoMethodError (protected method `protected_method' called for A:Class)
たしかに、継承関係にないクラス C からは、protected_method は呼び出せていません。
また、インスタンスメソッドからの呼び出しですが、同じクラスのインスタンスでも、継承されたクラスのインスタンスでも、protected
なクラスメソッドは呼び出せません。
obj_A = A.new obj_A.call_public_method # => "called public_method." obj_A.call_protected_method # => NoMethodError (protected method `protected_method' called for A:Class) obj_A.call_private_method # => NoMethodError (private method `private_method' called for A:Class) obj_B = B.new obj_B.call_public_method # => "called public_method." obj_B.call_protected_method # => NoMethodError (protected method `protected_method' called for A:Class) obj_B.call_private_method # => NoMethodError (private method `private_method' called for A:Class)
ちょっと意外です。直観的には呼び出せそうな感じがしてたんですが。。
どうやって使い分けるか?
外から呼び出す必要のあるものは、デフォルトの public で定義し、クラス内の内部処理でしか使えない、外から呼び出されるとかえって問題になるような場合は private にする、という使い分けだと思います。
protected に関しては、特異クラスで定義することはできますが、使わない方法を考える方が良いような感じです。( protected_class_method
で定義できないので、使う想定をあまりされてないんじゃないかなーと。)