その他
    ホーム 技術発信 DoRuby includeとextend
    includeとextend
     

    includeとextend

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    今回は、includeとextendについて調べてみました。

    include, extendとは

     include,extendは、モジュールのメソッドをクラスに読み込む機能を持っています。異なる点は、includeがインスタンスメソッド、extendがクラスメソッドとしてクラスに読み込みます。コードでは、以下のようになります。

    module Dog
      def dog
        'Bow wow'
      end
    end
    
    class IncludeDog
      include Dog
    end
    
    class ExtendDog
      extend Dog
    end
    
    include_dog = IncludeDog.new
    include_dog.cry
    => "Bow wow"
    
    ExtendDog.cry
    => "Bow wow" 
    

     extendは、クラスのインスタンスに対して実行することもできます。この場合、実行したインスタンスにのみ、extendしたモジュールのメソッドが使用することができます。クラスのインスタンスに対してextendした場合は、特異メソッドとして取り込まれます。

    module Dog
      def cry
        'Bow wow'
      end
    end
    
    class Animal
    end
    
    animal = Animal.new
    animal.extend Dog
    animal.cry
    =>"Bow wow"
    

     includeにもextendと同様に、インスタンスを使用した方法があります。

    animal = Animal.new
    class << animal
      include Dog
    end
    animal.cry
    =>"Bow wow"
    

    モジュール読み込みとオーバーライドについて

     モジュールとクラスに同じメソッドが存在した場合、include, extendを行うことでどちらのメソッドがオーバーライドされるのか気になったので調べてみました。結果は、クラス内でinclude, extendした場合は、クラスで定義したメソッドが呼び出され、クラスのインスタンスを使用してinclude,extendした場合は、モジュールで定義したメソッドが呼び出されました。

    enter image description here

     クラス内でinclude, extendした場合に、クラスのメソッドを上書きしてモジュールのメソッドを呼び出す方法が無いか調べてみると、includeの代わりにprependを使用することで解決できることが分かりました。includeとprependの違いは、継承される順番が異なることです。includeの場合だと「クラス < モジュール」、prependだと「モジュール < クラス」となります。

    module Dog
      def cry
        'module method'
      end
    end
    
    class PrependDog
      prepend Dog
    
      def cry
        'prepend dog'
      end
    end
    
    prepend_dog = PrependDog.new
    prepend_dog.cry
    => "module method"
    
    # PrependDogの親クラス一覧 一番左が自分のクラス、右が親クラス
    PrependDog.ancestors
    => [Dog, PrependDog, ..., Object, BasicObject]
    

    まとめ

    今回は、includeとextendについてまとめてみました。

    • インスタンスメソッドとして取り込むならinclude
    • クラスメソッドとして取り込むならextend
    • インスタンスに対してinclude, extendを行うことでクラスのメソッドを上書きする(メソッド名が重複する場合)
    • prependで、クラスのメソッドを上書きする(メソッド名が重複する場合)