オープンクラスとは
いつでも既存のClassを再オープンして、その場で修正出来ること。
オープンクラスの例
新規メソッドを作成
※Stringクラスにfooメソッドを追加してみる
class String
def foo
self + 'foo!'
end
end
'My name is '.foo #=> "My name is foo"
既存のメソッドを上書き
※superclassメソッドを上書きしてみる
# 通常の動作
String.superclass #=> Object
class String
def superclass
'superclass!'
end
end
# 上書き後の動作
String.superclass #=> "superclass!"
オープンクラスの問題点
既存のメソッドを上書きしてしまうことが可能なので、他の部分で上書きしたメソッドが使用されていた場合、エラーもしくは想定外の動作
をしてしまいます。
このように、オープンクラスにより既存のメソッドを置き換えることをモンキーパッチと言います。
モンキーパッチは善?悪?
結論を先に言うと、ケース・バイ・ケース
です。
意図的にモンキーパッチした場合は、悪いことでは無いので、「善」と言えます。
逆に、意図していない状態でモンキーパッチしてしまった場合は、「悪」と言えてしまいます。
注意点
オープンクラスを用いる際は、、
- 全ての関連するデータが保持しても問題のない機能か見極める
- テストコードを書く
- 拡張Stringクラスを定義
- (Ruby2.0以上の場合は)Refinementsを用いる
上記のポイントを意識することが重要です!
Refinementsとは
指定したクラスまたはモジュールだけに対して、ブロックで指定した機能を提供できるモジュールを定義します。これは、既存の機能を局所的に修正したい場合などに用いる事ができます。
using
は「ここ以降でRefinementsが有効」という意味で、モジュールに対して適用します。
refine
はRefinementsとして拡張するclassの指定です。
# Fooクラスの定義
class Foo
# fooメソッドの定義
def foo
puts "Foo#foo"
end
end
# Barモジュールの定義
module Bar
# FooクラスをRefinementsとして拡張
refine Foo do
def foo
puts "Bar::Foo#foo"
end
end
end
f = Foo.new
f.foo # => "C#foo"
# ここ以降でRefinementsが有効
using Bar
f = Foo.new
f.foo # => "Bar::Foo#foo"
終わりに
オープンクラスは、便利な分、ダークサイドも併せ持つメタプログラミングなので、理解して使うことが大事だと思いました。しかし、武器にすれば心強い。。!