「Mix-in(ミックスイン)」を行うことで、インクルードしたクラスの上の階層に動作を定義できますので結果的に仮想的なスーパークラスとして作用してくれます。まずは↓のスクリプトをみていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
module Mix def info puts self.class end end class Class1 include Mix end class Class2 end |
今回は「Class1」にモジュール「Mix」をインクルードして、「Class2」にはインクルードしていません。インクルードされているかどうかを調べるには「include?」メソッドを使います。
記述方法は「調べるクラス名.include?(モジュール名)」で調べるとブーリアン値が返ってきます。
1 2 |
p Class1.include?(Mix) p Class2.include?(Mix) |
true false
Contents
インクルード時の継承関係
インクルードされたクラスとされていないクラスの継承関係の違いを見ていきましょう。
1 2 3 4 |
p Class1.ancestors p Class1.superclass p Class2.ancestors p Class2.superclass |
[Class1, Mix, Object, Kernel, BasicObject] Object [Class2, Object, Kernel, BasicObject] Object
「ancestors」メソッドを使うと継承関係にある全てのクラスの表示ができます。処理を実行する場合この表示の中で左から順番にメソッドを検索して最初に見つかったものを実行するようになっています。
実行結果の1行目をみてみると「Class1」と「Object」の間に「Mix」が入っていて「Mix」から「Class1」に継承できていることがわかります。
BasicObject→Kernel→Object→Mix→Class1
ちなみに「Object」クラスは全てのクラスのスーパークラスとなり、「Kernel」モジュールをインクルードしているので継承関係は「Kernel→Object」となります。
「BasicObject」クラス「Ruby1.9」以降に登場しているようで、「Object」クラスのスーパークラスとなっています。
参考:BasicObject
「superclass」メソッドを実行するスーパークラスが返ってきます。今回の場合スーパークラスは「Object」であることがわかります。
クラスを継承した場合
蛇足かとは思いますが、定義したクラスを継承し、念のためインクルードした場合についてもみていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
module Mix def info puts "Mix!!!" end end module Mix2 def info puts "Mix2!!!" end end class Class1 include Mix end class Class2 < Class1 include Mix2 end p Class2.include?(Mix) p Class2.include?(Mix2) p Class2.ancestors p Class2.superclass |
true true [Class2, Mix2, Class1, Mix, Object, Kernel, BasicObject] Class1
「Class1」を「Class2」に継承したので実行結果の1、2がちゃんと「true」で返ってきていて、モジュール「Mix」「Mix2」がインクルードされていることがわかります。
継承関係でもClass1→Mix2→Class2となって、Class1がClass2に継承されて更に、インクルードしたMix2モジュールもそこに挟み込まれるように継承が行われています。これでClass1の機能を継承しつつ、「Mix2」モジュールからも機能を追加することができるようになります。
メソッド実行時の検索順
メソッド名が同一の場合
クラスとMix-inしたモジュールで両方とも同じ名前があった場合は、検索順で早いクラスの方が実行されます。今回は両方とも「mix_in」という同名のメソッドがありますが、検索順で早い「Class1」のメソッドが実行されることになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
module Mix def mix_in puts "module→Mix" end end class Class1 include Mix def mix_in puts "class→Class1" end end p Class1.ancestors Class1.new.mix_in |
[Class1, Mix, Object, Kernel, BasicObject] class→Class1
複数インクルードした場合
複数のモジュールがクラス内にインクルードされている場合は、後からインクルードしたメソッドから実行されます。今回は「Mix2」のインクルードが後になっているので「Mix2」モジュールから検索が行われてメソッドが実行されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
module Mix1 def mix_in puts "Mix1" end end module Mix2 def mix_in puts "Mix2!!" end end class Class1 include Mix1 include Mix2 end p Class1.ancestors Class1.new.mix_in |
[Class1, Mix2, Mix1, Object, Kernel, BasicObject] Mix2!!
インクルードが入れ子の場合
入れ子の状態でMix-inしている場合は、下記のようになります。「Class1」にモジュール「Mix2」がインクルードされているのでまずはそこから検索が行われ、モジュール「Mix2」にインクルードしている「Mix1」モジュールが検索されることになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
module Mix1 def mix_in puts "Mix1" end end module Mix2 include Mix1 def mix_in puts "Mix2" end end class Class1 include Mix2 end p Class1.ancestors Class1.new.mix_in |
[Class1, Mix2, Mix1, Object, Kernel, BasicObject] Mix2
同じモジュールを複数回インクルードした場合
複数回インクルードした場合では、2回目以降はスルーされます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
module Mix1 def mix_in puts "Mix1" end end module Mix2 def mix_in puts "Mix2" end end class Class1 include Mix1 include Mix2 include Mix1 end p Class1.ancestors Class1.new.mix_in |
[Class1, Mix2, Mix1, Object, Kernel, BasicObject] Mix2
この場合で後から「Mix1」を再度入力しても検索順では「Mix2」モジュールが優先されるので、「Mix1」を優先させたい場合は初めの「Mix1」のインクルードを削除する必要があります。
extendメソッド
extendメソッドを使うと、特異クラスにモジュールをインクルードしてくれます。こうすることによってメソッドを特定のオブジェクトで使えるようになります。また、普通に特異クラスにてモジュールをインクルードしても同様に機能の追加ができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
module Mix def mix_in puts "Mix!!" end end class Test_class end class1 = Test_class.new class2 = Test_class.new class3 = Test_class.new # extendメソッドを使った場合 class1.extend(Mix) # 特異クラスでインクルードした場合 class << class2 include Mix end class1.mix_in #=> Mix!! class2.mix_in #=> Mix!! class3.mix_in #=> NoMethodError |
extendメソッドでクラスメソッドを追加
先程の場合はオブジェクトにextendメソッドを使い、モジュールをインクルードしましたが、下記の17、18行目のようにクラスオブジェクトにextendメソッドを使うとクラスメソッドを追加することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
module Mix def say_mix puts "Mix!!" end end class Test_class end obj = Test_class.new # オブジェクトにメソッドを追加 obj.extend(Mix) obj.say_mix # クラスオブジェクトにメソッドを追加 Test_class.extend(Mix) Test_class.say_mix |
【整理】クラス内でextendまたはincludeした場合
「ClassMethod」モジュールをextendし、「InstanceMethod」モジュールをincludeした場合をみていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module ClassMethod def say_c puts "c" end end module InstanceMethod def say_i puts "i" end end class TestClass extend ClassMethod include InstanceMethod end TestClass.say_c test_i = TestClass.new test_i.say_i |
c i
上記の場合をみてみるとextendするとクラスメソッドが追加され、includeするとインクルードメソッドが追加されていることがわかります。
コメントを残す