[Ruby] メタプログラミングの入り口、オープンクラスを理解する

More than 5 years have passed since last update.


概要

メタプログラミング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