4
4

More than 5 years have passed since last update.

Rubyでprotectedなクラスメソッドを作る(疑問もあるけど)

Posted at

ひょんな拍子に、Rubyでprotectedなクラスメソッドを作る機会が生じましたが、ちょっとハマる箇所が出てきましたので、メモ程度に書き残しておきます。

Rubyのprotected

C++やJavaのprotectedと違って、Rubyのprotectedは、「selfがそのメソッドを定義したオブジェクトの場合」だけメソッドを呼べる、という意味合いです。@tbpgr さんのこちらの記事に詳しいです。

クラスメソッドをprotectedにすると

つまり、クラスメソッドをprotectedにすると、「クラスオブジェクト」自体がselfになる場面、すなわちそのクラスやサブクラスのクラスメソッド内、あるいは特異クラスの中からしかアクセスできなくなります。呼べる箇所はかなり限定的です。

どんなときに使う?

なかなか使う機会がないようにも思えますが、newprotectedにしてみると、外部からは別のクラスメソッドでインスタンスを取るしかない、というクラスを作れます。もちろんそれだけであればprivateでもいいのですが、継承関係などを考えているとprotectedでうまくいく、という例がありました(ただし、これでせいとうなのかはちょっと判断がついていません)。

実際にしてみる方法

Rubyでメソッドのアクセス権を変える方法としては、大分すれば2つあります。

  • publicprivateprotectedを引数なしで使って、状況を切り替えたうえでメソッドを定義する
  • これらのメソッドにシンボルを渡して、そのメソッドだけ可視性を切り替える

クラスメソッド用に、private_class_methodpublic_class_methodが用意してありますが、需要がないからかprotected_class_methodは用意してありません。そして、newなどは親クラスの側で用意してあるので、protectedな雰囲気の中で再定義するわけにも行きません。ということで、class << self; protected :new; endというように特異クラスをオープンすることになりました。

疑問~子クラスのが呼べるけど、どうなの?

ただ、これを書くために改めて調べていたのですが、やはり疑問が出てきました。抽象的な親クラスにいくつかの子クラスがある状況で、親クラスでprotectedしたとはいえ、子クラスのクラスメソッドを親クラスのクラスメソッド内から呼べてしまっているのです。これは意図した動作なのか、はたまたたまたま動いているだけなのかわからず、気持ち悪い状態に陥っています。

問題のコード

class AbstractParent
  class << self; protected :new; end
  def self.some_method
    # 実際には状況によって子クラスは入れ替わる
    klass = Child1
    obj = klass.new # エラーにならない
    # 後略
  end
end

class Child1 < AbstractParent; end
class Child2 < AbstractParent; end

4
4
0

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
4
4