概要
メタプログラミングRuby勉強録。
今回のトピックはメタプログラミングの代名詞の一つ、オープンクラスです。
オープンクラスとは
既存するクラスを好きな場所で再オープンし、
メソッド修正・追加など任意の変更を加えられる機能のこと。
例えば
以下のような文字列操作メソッドがあったとします。
def love_ruby(str)
str + ' I love Ruby!'
end
love_ruby('My name is kidachi.')
=> "My name is kidachi. I love Ruby!"
取りあえずトップレベルに定義しましたが、
オブジェクティブにするため、クラスに所属するようにしましょう。
そこで、オープンクラスを使ってStringクラス自体を改変します。
class String
def love_ruby
self + ' I love Ruby!'
end
end
'My name is kidachi.'.love_ruby
=> "My name is kidachi. I love Ruby!"
これでStringクラスを改変できました。
以後、あらゆる文字列がlove_rubyメソッドを利用できます。
'Foo!'.love_ruby
=> "Foo! I love Ruby!"
'Bar!'.love_ruby
=> "Bar! I love Ruby!"
'I love Ruby!'.love_ruby
=> "I love Ruby! I love Ruby!"
これがオープンクラスの力です。
また、新規メソッドを定義するだけでなく、
既存のメソッドを書き換えてしまうことも可能です。
例えば、文字列操作でよく使うupcaseを書き換えてみましょう。
# 本来のupcase
'I love Ruby!'.upcase
=> "I LOVE RUBY!"
# オープンクラスによりupcase自体を書き換え
class String
def upcase
self + ' I love Ruby!'
end
end
# 改変後のupcase
'I love Ruby!'.upcase
=> "I love Ruby! I love Ruby!"
いかがでしょうか。
ここまでで、その強力さと同時に危険さが伝わったかと思います。
いくらRubyのことが好きでも、大文字変換が期待されるメソッドを
まったく関係のない機能で上書いてしまうのは、ただのテロですよね。
つまり、オープンクラスは、
その気になればStringやArrayと言ったコアなクラスも
思いのままに破壊できてしまう危険性
を伴っているということ。
よってオープンクラスを使う際は以下のルールを守るべきです。
- 全ての文字列が保持しても問題のない汎用的な機能か見極める
- 充分にテストする
つまり上記例で言うと、upcaseの書き換えはもちろん、
汎用性に欠けるlove_rubyメソッドも本来オープンクラスで
追加するようなものではないということですね。
代替案
love_rubyメソッドのように、限定的な場面でのみ
使いたい/使われるべき機能の場合は、
- 大人しく拡張Stringクラスを定義する
- (Ruby2.0以上の場合は)refinmentsを用いる
という選択肢があります。
※refinments
オープンクラスの影響を特定範囲に限定出来る。
ただし、Ruby2.0で実験的実装、2.1で正式採用という
非常に新しい機能なので、利用の際は最新情報キャッチアップの上でどうぞ!
参考)Refinementsとは何だったのか
http://magazine.rubyist.net/?0041-200Special-refinement
モンキーパッチ
オープンクラスにより既存のメソッドを置き換えることをモンキーパッチと言います。
また、特に、意図せずに既存メソッドを書き換えてしまった事象のことや、
それゆえにマイナスイメージを伴うものを意味する向きもあります。
もちろん普通に便利なものとして使うことも。
明確な定義はなく、細かいところはその場の文脈で変わる様です。
上記のString#upcaseの書き換えは(良くない)モンキーパッチですね。
結論
オープンクラス(に限らずメタプログラミング全般)は
- 既存クラスをいつでも思いのままに修正出来る利便性
- ルールを破壊してしまう危険性
を備えています。
メタプログラミングの入り口に立った私たちは、
今後常にMatzのことばを頭の片隅に置いておきたいですね。
「Rubyは君を信頼する。Rubyは君を分別のあるプログラマとして扱う。
Rubyはメタプログラミングのような強力な力を与える。
ただし、大いなる力には、大いなる責任が伴うことを忘れてはいけない」
-「メタプログラミングRuby」序文より。
以下に続く
[Ruby] method_missing()を実用レベルで理解する
http://qiita.com/kidachi_/items/75ae4a29c99a79816384