module / include / extend / mixin
| 対応: | Ruby 1.8(2003) |
|---|
Rubyのモジュール(module)とミックスイン(mixin)の仕組みです。複数のクラスに共通の機能を持たせる多重継承の代替手段として使われます。
構文
# モジュールの定義
module モジュール名
def インスタンスメソッド
処理
end
def self.モジュール関数
処理
end
end
# include: モジュールのメソッドをインスタンスメソッドとして追加する
class クラス名
include モジュール名
end
# extend: モジュールのメソッドをクラスメソッドとして追加する
class クラス名
extend モジュール名
end
# prepend: include より前にメソッド探索チェーンに挿入する(メソッドの前処理に使う)
class クラス名
prepend モジュール名
end
# 特定のオブジェクトにだけモジュールを extend する(シングルトンメソッド)
オブジェクト.extend(モジュール名)
メソッド一覧
| 構文 / メソッド | 概要 |
|---|---|
| module モジュール名 | モジュールを定義します。インスタンス化できず、クラスと異なり継承もできません。 |
| include モジュール名 | モジュールのメソッドをクラスのインスタンスメソッドとして追加します。 |
| extend モジュール名 | モジュールのメソッドをクラスのクラスメソッドとして追加します。 |
| prepend モジュール名 | モジュールをメソッド探索チェーンの先頭に挿入します(オーバーライドの前処理に便利です)。 |
| include? / ancestors | クラスがどのモジュールをインクルードしているか確認できます。 |
サンプルコード
『Greetable』は挨拶機能を提供するモジュール、『Loggable』はログ機能を提供するモジュールです。
module_include_extend.rb
module Greetable
def greet
"こんにちは!私は#{name}です。"
end
def farewell
"さようなら!#{name}でした。"
end
end
module Loggable
def log(message)
puts "[LOG] #{self.class}: #{message}"
end
end
# include でインスタンスメソッドとして追加する
class Person
include Greetable
include Loggable
attr_reader :name
def initialize(name)
@name = name
end
end
class Robot
include Greetable
attr_reader :name
def initialize(name)
@name = name
end
end
# モジュールのメソッドが使える
p = Person.new("綾波レイ")
puts p.greet # こんにちは!私は綾波レイです。
puts p.farewell # さようなら!綾波レイでした。
p.log("起動しました") # [LOG] Person: 起動しました
r = Robot.new("初号機")
puts r.greet # こんにちは!私は初号機です。
# モジュールの確認
puts Person.ancestors.inspect
# [Person, Loggable, Greetable, Object, Kernel, BasicObject]
ruby module_include_extend.rb こんにちは!私は綾波レイです。 さようなら!綾波レイでした。 [LOG] Person: 起動しました こんにちは!私は初号機です。 [Person, Loggable, Greetable, Object, Kernel, BasicObject]
extend と prepend の実践例
extend_prepend.rb
# extend: クラスメソッドとして追加する
module ClassMethods
def find_by_name(name)
puts "DB検索: #{name}"
{ name: name, series: "エヴァンゲリオン" }
end
end
class Pilot
extend ClassMethods # クラスメソッドになる
end
Pilot.find_by_name("碇シンジ") # DB検索: 碇シンジ(クラスメソッドとして呼び出します)
# prepend: メソッドの前処理に使う(オーバーライド前に挿入)
module Timestamp
def save
puts "[#{Time.now}] 保存前の処理"
super # 元の save メソッドを呼び出す
end
end
class Record
prepend Timestamp # Record#save より先に Timestamp#save が呼ばれる
def save
puts "保存しました"
end
end
Record.new.save
# [2026-...] 保存前の処理
# 保存しました
ruby extend_prepend.rb DB検索: 碇シンジ [2026-03-29 ...] 保存前の処理 保存しました
よくあるミス1: includeとextendの混同
include と extend を混同すると、インスタンスから呼んだときに NoMethodError になります。
module_extend_ng.rb
# NG: include と extend を混同する
module Greetable
def greet
"こんにちは!"
end
end
class Person
extend Greetable # extend なのでクラスメソッドになる
end
p = Person.new
p.greet # NoMethodError: undefined method 'greet' for an instance of Person
ruby module_extend_ng.rb module_extend_ng.rb:12:in '<main>': undefined method 'greet' for an instance of Person (NoMethodError)
module_include_ok.rb
# OK: インスタンスメソッドにしたい場合は include を使う class Person include Greetable # include でインスタンスメソッドになる end Person.new.greet # "こんにちは!"
ruby module_include_ok.rb こんにちは!
『include』はインスタンスメソッドの追加、『extend』はクラスメソッドの追加です。混同すると NoMethodError になるため、使い分けに注意してください。
よくあるミス2: モジュールのインスタンス変数
モジュールにインスタンス変数を持たせると、複数のクラスが include した場合に @count が混在する予期しない動作になります。モジュールにはステートレスなメソッドのみを定義してください。
# NG: モジュールにインスタンス変数を持たせると予期しない動作になる
module Counter
def increment
@count ||= 0
@count += 1 # インクルード先のインスタンス変数に影響する
end
end
# 複数のクラスが Counter を include した場合、@count が混在する
概要
Rubyは多重継承を持ちませんが、モジュールのミックスインによって複数のクラスに共通機能を持たせることができます。モジュールは「機能の集まり」としてクラスとは別に定義し、必要なクラスに『include』または『extend』で取り込みます。
『include』はモジュールのメソッドをインスタンスメソッドとして追加し、『extend』はクラスメソッドとして追加します。メソッド探索の順序はMRO(Method Resolution Order)と呼ばれ、『ancestors』で確認できます。MROとは、メソッドを呼び出した際にRubyがどの順序でモジュールやクラスを探すかの優先順位です。後からインクルードしたモジュールが探索順で前になります(スタック構造)。
モジュールはインスタンス化できません。モジュールに状態(インスタンス変数)を持たせると、インクルード先のクラスのインスタンス変数と混在するため混乱を招きます。モジュールにはなるべくステートレスなメソッドのみを定義するとよいでしょう。
標準ライブラリの『Comparable』モジュールを使ったカスタム比較については『Comparable / <=>』を、継承の仕組みは『継承 / super』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。