Class#includeとModule#include

いまさらながら、今年初投稿の寺岡です。

昨日は神戸.rbにおじゃまして、やっぱりrubyが好きだなぁと再確認してきました。
ちょうどメタプロのハンズオンをやってきたので、ちょっと前に嵌ったメタプロ関連の小ネタを書いてみたいと思います。

ModuleはClassだけでなく、Moduleにもインクルードできる

rubyのモジュールは、クラスにインクルードすることでMixInを実現できる機能です。
rubyの最大の特徴といっても過言ではなく、メタプロには欠かせない要素ですね。

一般的に「モジュールはクラスにインクルードするもの」と思われがちですが、
実はモジュールに対してモジュールをインクルードすることも出来るのです。

上記の例のように、HogeModuleに対してSayHogeモジュールをMixInすることによって、
HogeModuleをインクルードしたクラスにSayHoge#hogeメソッドを提供することができました。

Class#includeとはちょっと違う

Module#includeは便利な機能ですが、Class#includeとは大きく違う点があります。

Class#includeでモジュールをインクルードした場合、既にインスタンス化されている
オブジェクトに対しても、モジュールの機能を追加することができます。

同じような感覚でModule#includeを使っていると、こんなエラーに出くわします。

上記のようにClassXにインクルードした後でFugaModuleにモジュールをインクルードしても、Xクラスには反映されません。

詳しく確認してみる

Module#include前後でどう変わるのか、もう少し詳しく確認するために以下のコードを実行してみます。

同じPiyoModuleをインクルードしているのに、piyoメソッドの実行結果が違いますね。
FirstとSecondに対するancestorsの結果を比べると、SecondだけAfterIncludeモジュールが追加されているのがわかります。

Module#includeは「モジュールがインクルードされる際に、追加でインクルードするモジュールを指定する」という挙動になります。
その効果は既にモジュールがインクルードされたクラスには波及しません。

Module#includeを使うときは順序に注意

Module#includeは複雑なモジュールを分割するときに便利ですが、
利用する場合は実行するモジュールがまだ他のクラスにインクルードされていないタイミングで実行するように調整したほうが良いでしょう。
既存のライブラリへのモンキーパッチなどの用途でModule#includeを使うのはおススメできません。

もちろんModule#prependも同じような挙動になるのでご注意を。

Comments are closed, but you can leave a trackback: Trackback URL.