モジュール(Module)はメソッドや定数をまとめるための仕組みです。クラスと似ていますが、インスタンスを作成できないという違いがあります。モジュールは主に「Mixin(ミックスイン)」によるクラスへの機能追加と、「名前空間」による名前の衝突防止の2つの用途で使われます。
Mixin(ミックスイン)
モジュールのメソッドをincludeでクラスに取り込むことをMixinと呼びます。Rubyは単一継承ですが、Mixinにより複数の機能をクラスに追加できます。
Ruby
module Greetable
def greet
puts "こんにちは、#{name}です!"
end
end
module Printable
def print_info
puts "=== 情報 ==="
puts to_s
puts "============"
end
end
class User
include Greetable
include Printable
attr_reader :name, :age
def initialize(name, age)
@name = name
@age = age
end
def to_s
"#{@name}(#{@age}歳)"
end
end
user = User.new('太郎', 25)
user.greet # Greetableモジュールのメソッド
user.print_info # Printableモジュールのメソッド実行結果
こんにちは、太郎です!
=== 情報 ===
太郎(25歳)
============include GreetableでGreetableモジュールのメソッドがUserクラスのインスタンスメソッドとして使えるようになります。複数のモジュールをincludeでき、これがRubyにおける多重継承の代替手段です。
名前空間としてのモジュール
モジュールを名前空間として使うと、クラス名の衝突を防げます。
Ruby
module Admin
class User
def initialize(name)
@name = name
@role = '管理者'
end
def to_s
"#{@name}(#{@role})"
end
end
end
module Customer
class User
def initialize(name)
@name = name
@role = '顧客'
end
def to_s
"#{@name}(#{@role})"
end
end
end
admin = Admin::User.new('管理太郎')
customer = Customer::User.new('顧客花子')
puts admin
puts customer実行結果
管理太郎(管理者)
顧客花子(顧客)同じUserというクラス名でも、Admin::UserとCustomer::Userとして区別できます。大規模なプロジェクトで名前の衝突を避けるために重要なテクニックです。
extendでクラスメソッドを追加
includeはインスタンスメソッドを追加しますが、extendはクラスメソッドとして追加します。
Ruby
module ClassInfo
def description
"#{name}クラス(#{instance_methods(false).length}個のメソッド)"
end
end
class Product
extend ClassInfo
attr_reader :name, :price
def initialize(name, price)
@name = name
@price = price
end
def info
"#{@name}: #{@price}円"
end
end
puts Product.description # クラスメソッドとして呼べる実行結果
Productクラス(2個のメソッド)実践的な使い方
Ruby
# 共通機能をモジュールで定義
module Validatable
def validate_presence(field, value)
if value.nil? || value.to_s.strip.empty?
puts "エラー: #{field}は必須です"
false
else
true
end
end
end
module Loggable
def log(message)
puts "[#{self.class.name}] #{message}"
end
end
class Order
include Validatable
include Loggable
attr_reader :product, :quantity
def initialize(product, quantity)
@product = product
@quantity = quantity
end
def process
return unless validate_presence('商品名', @product)
return unless validate_presence('数量', @quantity)
log("注文処理開始: #{@product} x #{@quantity}")
log("注文処理完了")
end
end
order1 = Order.new('ノートPC', 2)
order1.process
puts '---'
order2 = Order.new('', 1)
order2.process実行結果
[Order] 注文処理開始: ノートPC x 2
[Order] 注文処理完了
---
エラー: 商品名は必須ですincludeとextendの使い分け
includeはモジュールのメソッドをインスタンスメソッドとして追加します。extendはクラスメソッド(または特定のオブジェクトの特異メソッド)として追加します。用途に応じて使い分けましょう。
モジュールはインスタンスを作れない
モジュールはクラスと違いnewでインスタンスを作成できません。モジュールは機能の「部品」であり、単独で使うものではなく、クラスにinclude/extendして使います。
まとめ
- モジュールはメソッドや定数をまとめる仕組み(インスタンスは作れない)
includeでモジュールのメソッドをインスタンスメソッドとして追加(Mixin)extendでクラスメソッドとして追加- 名前空間として使い、クラス名の衝突を防止できる
- Mixinは単一継承のRubyで多重継承の代替手段となる