概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Class Methods In Ruby: a Thorough Review & Why I Define Them Using class << self
- 原文公開日: 2017/11/24
- 著者: Eliav Lavi
Rubyのクラスメソッドをclass << selfで定義している理由(翻訳)
https://pixnio.com/nature-landscapes/winter/landscape-sky-winter-snow-ice-water-tree-nature-outdoor-reflectionより
クラスメソッドは私の同僚の間で常に議論や反論の種になっています。クラスメソッドは的確かつ有用と考える人もいますが、実際にはコードの読みやすさや管理のしやすさを損ないがちな邪魔者だと感じる人もいます。私はRubyのオブジェクト指向的な本質を信奉しており、オブジェクトで考えることを(読むのも!)好んでいますが、私には後者が真実になる傾向があることに気づきました。よく言われるように、クラスメソッドがどうしても必要になることもあります。ファクトリーメソッドから、ActiveRecordモデルのカスタムクエリメソッドで使われる複雑なメタプログラミングインターフェイスにいたるまで、クラスメソッドを全否定することはできません。もちろんクラスメソッドの利用は控えめにすべきではありますが(詳しくはCode Climateのこちらの良記事をご覧ください)。
本記事ではクラスメソッドそのものの良し悪しについては言及せず、クラスメソッドが必要になった場合のクラスメソッドのスタイル上の記法について議論します。
コーディングスタイルとスタイルガイド
Rubyスタイルガイドでは、クラスメソッドはdef self.method
という記法を用いるのが望ましいとされています。このスタイルでは、より明示的な def ClassName.method
という記法が批判されていますが、より謎めいたclass << self
という記法も補助的にサポートされています。記法の実際の表示については該当のセクションをご覧ください。
組織内でスタイルガイドを共有し、それに従うことは重要です。Sandi Metz氏の良記事では次のように指摘されています。
(略)スタイル上の選択は多くの場合任意であり、純粋に個人の好みの問題です。スタイルガイドを選択することは、ほとんど重要でない点で意見が割れてしまった場合の合意を形成するということです。スタイル(そのもの)が重要だということではなく、スタイルが揃うことが重要です。
Why we argue styleより
この指摘は実にもっともです。しかし、スタイルを正しく選択できることもやはり重要です。服装と同じく、コードのスタイルには開発者としての信条や価値観や哲学が反映されるのですから、この問題についても十分理解が必要です。私たちの誰もが多くのクラスメソッドを定義していますが、果たして私たちはクラスメソッドの動作を理解しているのでしょうか。
https://images.askmen.com/1080x540/2015/11/06-042951-men_s_fashion_must_haves.jpgより
シングルトンクラス
上の問いに答えるために、Rubyのオブジェクトモデルについて取り急ぎ調べる必要があります。一般に、Rubyのメソッドはクラスに保存され、データはオブジェクト(クラスのインスタンス)に保存されます。これはかなり一般的な知識なので、次の例でもう少し追ってみましょう。
an_array = [1, 5, 10]
an_array.average
を実行すると、Array
やそのスーパークラスにaverage
メソッドが定義されていないので、次のようにNoMethodError
エラーが出力されます。
an_array.average
# NoMethodError: undefined method `average' for [1, 5, 10]:Array
Array
にモンキーパッチを適用してaverage
メソッドを定義してもよいのですが、このメソッドはan_array
以外では不要なのであれば、次のようにすることもできます。
def an_array.average
reduce(:+) / count.to_f
end
これで次のように動きます。
an_array.average
# => 5.333333333333333
同じメソッドをArray
の別のインスタンスで実行しようとすれば、次のようにまたNoMethodError
が出力されます。
another_array = [1, 3, 7]
another_array.average
# => NoMethodError: undefined method `average' for [1, 3, 7]:Array
この理由は、Rubyが舞台裏でaverage
メソッドを特殊なクラスに保存しているからです。an_array
だけがその特殊なクラスを指しています。つまり自身のシングルトンクラスです。
an_array.singleton_class
# => #<Class:#<Array:0x007fcf27848750>>
an_array.singleton_methods
# => [:average]
Rubyでは、どんなクラスのどんなインスタンスにも必ず自身のシングルトンクラスがあり、そこにシングルトンメソッドが保存されます。先ほど定義したメソッドもシングルトンメソッドであり、シングルトンクラスに保存されています(ただし例外として、Numeric
オブジェクトはこれに該当しません)。
あるオブジェクトのメソッドを呼び出すと、Rubyは最初にそのオブジェクトのシングルトンクラスでメソッドを探索し、それから通常のクラスや先祖クラスのチェインを探索します。
クラスメソッドはすなわちシングルトンメソッドである
Rubyではクラスもオブジェクトなので、クラスメソッドはClass
の特定のインスタンス上に定義された単なるメソッドです。次の例で考えてみましょう(gist)。
class Example
def self.a_class_method; end
def an_instance_method; end
end
実際の動作を見れば理屈はすぐわかります。
Example.is_a? Object
# => true
Example.class
# => Class
Example.singleton_class
=> #<Class:Example>
Example.instance_methods(false)
=> [:an_instance_method]
Example.singleton_class.instance_methods(false)
=> [:a_class_method]
instance_methods
の引数をfalse
で呼び出すと、継承されたメソッドを除いたメソッドリストが返されます(instance_methods
)。
記法を選ぶ
ついにRubyのクラスメソッドを正確に理解できました。これでコーディング方法について詳しく議論する準備が整いました。本記事のタイトルでおわかりのように、私はdef self.method
記法よりclass << self
記法を好んでいます。理由はおわかりでしょうか?
私はメソッドを、それらが属しているクラスの内部で定義したいのです。class << self
記法ならこのアプローチ、すなわちメソッドを実際のシングルトンクラスのスコープ内で定義していることがはっきりと伝わります。しかしdef self.method
を使うと、複数のスコープにわたってメソッドを定義していることになります。通常のクラススコープの中にいるにもかかわらず、コードのどの場所でも特定のインスタンスにメソッドを定義できるRubyの機能を使っているのです。クラス定義内におけるself
は、その場所における現在の(クラス自身などの)Class
インスタンスを指します。このため、def self.method
を使うとスコープを飛び越えることになり、私にはこれがおかしいと感じられるのです。
def self.method
記法にはもうひとつ疑問があります。その理由は、privateやprotectedなメソッドを定義できることです。Rubyには、クラスメソッドをprivateとして宣言するためのprivate_class_method
メソッドが用意されていますが、protectedメソッドについては同等のものがありません。また、privateなクラスメソッドでは、各メソッドを個別に宣言しなければなりません(たとえば、クラスの途中で単純にprivate
を用いることはできません: これはそのクラスのインスタンスメソッドに適用されるからです)。要は、class << self
の方が実際にはより明確だということです。
予想される反論
Sandi Metzの記事で指摘されているように、スタイルに関する議論はときとして開発者の間で感情的にこじれてしまうことがあります。class << self
記法について予想される反論として次のようなものが考えられます。
- 大きなクラスだとどれがクラスメソッドだかわかりにくい: 私もその点にはまったく同意です。コード量が450行もある神クラスなら、まずリファクタリングしてより小さなクラスに分解しつつ
def self.method
記法は変えないようにすべきでしょう。いくらスクロールしても終わらないほどコードが多ければ、スコープが変わる場所を簡単に見落としてしまいます。しかしそれでもそのクラスはリファクタリングすべきです。 -
明快でなくなる: これは一方的な見方ですし、根拠も不十分です。
def self.method
はまったくもって明快ではありません。前述のようにdef self.method
の本当の意味を理解できれば、def self.method
はスコープが混じるために実際には曖昧になってしまいます。この動作の背後にある理論を把握することが理解の助けになります。 -
素直にスタイルガイドに従えばいいんじゃね?: これに対する私の回答は、動作の詳細に注意を向けることが多くの場合重要だということです。これは重大な問題ではありませんし、
def self.method
は「コードの匂い」にも該当しません。実際、これ自体議論になるかどうかというトリビアな問題です。それぞれの選択肢について動作を学んだり知識を得たりすることで、初めて議論に値打ちが生まれます。さらに、class << self
記法は私にとってよりよいものであり、RubyやRubyのオブジェクト指向を安定して理解できると信じています。最後に、スタイルガイドは変更/改訂される(ただし慎重にですが)可能性があることもどうかお忘れなく。
本記事が皆さんにとって有益であることを願っています。このトピックについてお気づきの点やご質問がありましたらコメント欄までどうぞ。本記事が面白い/有益だと思っていただけた方は、ぜひ👏ボタンをクリックして応援してください。