オープンクラスの技法と、クラス継承ツリーの法則


メタプログラミング第二章を読んで学んだことをまとめます。

オープンクラス

Rubyのクラスは、後からオープンしてメソッドを追加することができます。

Class String
  def sayHello
    'Hello'
  end
end

s='hoge'.sayHello -> Hello

モンキーパッチの注意点
既存のメソッドを誤って上書きすると、予期せぬ挙動を起こすことがあるので、メソッド一覧を見る等してメソッドの衝突を避けること。

自動ユニットテストが、モンキーパッチの副作用を防ぐ強力な解法の一つです。

また、Refinementsを使うことで、モンキーパッチの影響箇所を局所的にすることができます。
Refinementの書き方をして既存クラスにメソッドを拡張した場合、明示的にusingを指定した場合にのみ拡張されたメソッドが有効になります。

module StringExtensions
  refine String do
    def sayHello
       puts 'Hello'
    end
  end
end

'hoge'.sayHello -> undefined method `sayHello' for "hoge":String (NoMethodError)

using StringExtensions

'fuga'.sayHello -> Hello


2.クラスのメタ情報

メソッド 効果
class そのインスタンスの型情報を取得
instance_methods(false) そのクラスのインスタンスメソッドの一覧を取得(引数がtrueなら、継承されたメソッドも取得)
instance_variables そのインスタンスの変数を取得

Rubyの継承ツリー

Rubyの継承ツリーは、 ancestorsでたどることができます。

irb(main):013:0> Array.ancestors
=> [Array, Enumerable, Object, Kernel, BasicObject]
irb(main):005:0> Class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]

Classの親クラスはなんとModuleです。Moduleにインスタンス生成用のメソッド(new,allocate,superclass)を追加したものがClassであるといえます。

メソッド探索

「右へ一歩、それから上へ」

インスタンスが呼び出したメソッドは、
1.まず自分が特異クラスが呼び出せるか
2.呼び出せなければ、自分のクラスが呼び出せるか
3.呼び出せなければ、自分の親クラスが呼び出せるか
4. 2.3を、継承ツリー一番上のBasicObjectまで続ける

モジュールの挿入位置

・includeモジュールは、継承チェーンで、インクルードされたクラスのすぐに配置される。
・prependモジュールは、継承チェーンで、インクルードされたクラスのすぐに配置される。

Kernelとは?

Kernelのメソッドは、どこからでも呼べるように思えます。

f = open('hoge.txt','r') #Kernel#open

しかし、Kernelは実はObjectにインクルードされているただのモジュールです。

ObjectクラスがKernelモジュールをインクルードしているので、すべてのオブジェクトの継承チェーンにKernelモジュールが含まれる。
だから、キーワードのように print などを使用することができるわけですね。

Rubyは、ベーシックなオブジェクト志向言語の一面を持っていますが、このようにオブジェクト志向を破壊する力を持ち合わせるのもまた一面です。

詳しく学びたい方は、「メタプログラミングRuby」を読んでみると良いでしょう、