my.code(); Logomy.code();
Ruby-7.モジュールとMix-in

my.code(); Logomy.code();

  • C++
    • 0.C++の世界へようこそ
    • 1.型システムと制御構造
    • 2.データ集合とモダンな操作
    • 3.ポインタとメモリ管理
    • 4.関数と参照渡し
    • 5.プロジェクトの分割とビルド
    • 6.クラスの基礎
    • 7.クラスを使いこなす
    • 8.継承とポリモーフィズム
    • 9.テンプレート
    • 10.STL ①:コンテナ
    • 11.STL ②:アルゴリズムとラムダ式
    • 12.RAIIとスマートポインタ
  • JavaScript
    • 0.JavaScriptへようこそ
    • 1.基本構文とデータ型
    • 2.制御構文
    • 3.関数とクロージャ
    • 4.'this'の正体
    • 5.オブジェクトとプロトタイプ
    • 6.クラス構文
    • 7.配列とイテレーション
    • 8.非同期処理①: Promise
    • 9.非同期処理②: Async/Await
  • Python
    • 0.環境構築と基本思想
    • 1.基本構文とデータ型
    • 2.リスト、タプル、辞書、セット
    • 3.制御構文と関数
    • 4.モジュールとパッケージ
    • 5.オブジェクト指向プログラミング
    • 6.ファイルの入出力とコンテキストマネージャ
    • 7.例外処理
    • 8.ジェネレータとデコレータ
  • Ruby
    • 0.rubyの世界へようこそ
    • 1.基本構文とデータ型
    • 2.制御構造とメソッド定義
    • 3.すべてがオブジェクト
    • 4.コレクション (Array, Hash, Range)
    • 5.ブロックとイテレータ
    • 6.クラスとオブジェクト
    • 7.モジュールとMix-in
    • 8.Proc, Lambda, クロージャ
    • 9.標準ライブラリの活用
    • 10.テスト文化入門
    • 11.メタプログラミング入門
  • Rust
    • 0.Rustの世界へようこそ
    • 1.基本構文と「不変性」
    • 2.関数と制御フロー
    • 3.所有権
    • 4.借用とスライス
    • 5.構造体とメソッド構文
    • 6.列挙型とパターンマッチ
    • 7.モジュールシステムとパッケージ管理
    • 8.コレクションと文字列
    • 9.エラーハンドリング
    • 10.ジェネリクスとトレイト
    • 11.ライフタイム
  • TypeScript
    • 0.TypeScriptへようこそ
    • 1.基本的な型と型推論
    • 2.オブジェクト、インターフェース、型エイリアス
    • 3.関数の型定義
    • 4.型を組み合わせる
    • 5.ジェネリクス
    • 6.クラスとアクセス修飾子
    • 7.非同期処理とユーティリティ型
my.code(); Logomy.code();

環境構築不要、その場で実践。

ut-code / my-code

Copyright © 2026 ut.code();

ut.code(); について
公式ウェブサイト公式 𝕏 アカウント

第7章: モジュールとミックスイン(オブジェクト指向の拡張)

Rubyのオブジェクト指向において、クラスの継承は「is-a」(〜である)関係を表現するのに適しています。しかし、「has-a」(〜を持つ)や「can-do」(〜ができる)といった振る舞い(ビヘイビア)を複数の異なるクラス間で共有したい場合があります。

他の言語では「インターフェース」や「トレイト」で解決するこの問題を、Rubyはモジュール (Module) と ミックスイン (Mix-in) という強力な仕組みで解決します。

モジュール (module) の2つの役割

module キーワードで定義されるモジュールには、大きく分けて2つの主要な役割があります。

  1. 名前空間 (Namespace): 関連するクラス、メソッド、定数を一つのグループにまとめ、名前の衝突(コンフリクト)を防ぎます。
  2. ミックスイン (Mix-in): メソッドの集まりを定義し、それをクラスに include することで、インスタンスメソッドとして機能を追加します。これはRubyの「多重継承」の代替手段です。

名前空間としてのモジュール

プログラムが大規模になると、異なる目的で同じ名前のクラス(例: Database::User と WebApp::User)を使いたくなることがあります。モジュールは、これらを区別するための「仕切り」として機能します。

名前空間内の要素には、:: (スコープ解決演算子) を使ってアクセスします。

ファイルを編集:module_example.rb
module AppUtilities
  VERSION = "1.0.0"
  
  class Logger
    def log(msg)
      puts "[App log] #{msg}"
    end
  end
  
  # モジュールメソッド (self. をつける)
  def self.default_message
    "Hello from Utility"
  end
end

# 定数へのアクセス
puts AppUtilities::VERSION

# モジュールメソッドの呼び出し
puts AppUtilities.default_message

# モジュール内のクラスのインスタンス化
logger = AppUtilities::Logger.new
logger.log("Initialized.")
ruby module_example.rb
ブラウザ上で動作するRuby3.4の実行環境です。
左上の実行ボタンを押して、このページ内のmodule_example.rbに書かれている内容を実行します。
1.0.0
Hello from Utility
[App log] Initialized.

ミックスインとしてのモジュール (include)

モジュールの最も強力な機能がミックスインです。これにより、クラスは継承ツリーとは無関係に、モジュールの振る舞い(インスタンスメソッド)を取り込むことができます。

include を使うと、モジュールはクラスの継承チェーン(祖先チェーン)に挿入されます。具体的には、include したクラスのスーパークラスの「直前」に挿入されます。

ファイルを編集:mix_in_example.rb
# 「飛ぶ」能力を提供するモジュール
module Flyable
  def fly
    puts "I'm flying! My speed is #{fly_speed}."
  end

  # このモジュールは、include したクラスが 
  # `fly_speed` メソッドを実装していることを期待している
end

# 「泳ぐ」能力を提供するモジュール
module Swimmable
  def swim
    puts "I'm swimming!"
  end
end

class Bird
  # fly_speed を実装
  def fly_speed
    "10km/h"
  end
end

class Duck < Bird
  include Flyable   # 飛べる
  include Swimmable # 泳げる
end

class Penguin < Bird
  include Swimmable # 泳げる (飛べない)
end

class Airplane
  include Flyable   # 飛べる (生物ではない)

  def fly_speed
    "800km/h"
  end
end

puts "--- Duck ---"
duck = Duck.new
duck.fly
duck.swim

puts "--- Penguin ---"
penguin = Penguin.new
# penguin.fly #=> NoMethodError
penguin.swim

puts "--- Airplane ---"
airplane = Airplane.new
airplane.fly
# airplane.swim #=> NoMethodError
ruby mix_in_example.rb
ブラウザ上で動作するRuby3.4の実行環境です。
左上の実行ボタンを押して、このページ内のmix_in_example.rbに書かれている内容を実行します。
--- Duck ---
I'm flying! My speed is 10km/h.
I'm swimming!
--- Penguin ---
I'm swimming!
--- Airplane ---
I'm flying! My speed is 800km/h.

Duck と Airplane は全く異なるクラス(Bird のサブクラスと、Object のサブクラス)ですが、Flyable モジュールを include することで fly メソッドを共有できています。

include vs extend

include と extend は、モジュールのメソッドをどこに追加するかが異なります。

  • include: モジュールのメソッドを、クラスのインスタンスメソッドとして追加します。
  • extend: モジュールのメソッドを、クラスのクラスメソッド(特異メソッド)として追加します。
ファイルを編集:extend_example.rb
module HelperMethods
  def info
    "This is a helper method."
  end
end

# --- include の場合 ---
class IncludedClass
  include HelperMethods
end

obj = IncludedClass.new
obj.info # インスタンスメソッドになる
# IncludedClass.info  #=> NoMethodError

# --- extend の場合 ---
class ExtendedClass
  extend HelperMethods
end

ExtendedClass.info # クラスメソッドになる
obj2 = ExtendedClass.new
# obj2.info  #=> NoMethodError
ruby extend_example.rb
ブラウザ上で動作するRuby3.4の実行環境です。
左上の実行ボタンを押して、このページ内のextend_example.rbに書かれている内容を実行します。
"This is a helper method."
"This is a helper method."

アクセスコントロール (public, private, protected)

Rubyのアクセスコントロールは、他の言語と少し異なる振る舞い、特に private の動作に特徴があります。

  • public (デフォルト)

    • どこからでも呼び出せます。レシーバ(object.)を省略しても、明示しても構いません。
  • private

    • レシーバを明示して呼び出すことができません。
    • self. を付けずに、クラス内部(またはサブクラス)からのみ呼び出せます。
    • 主にクラス内部の詳細を隠蔽(カプセル化)するために使われます。
  • protected

    • private と似ていますが、同じクラス(またはサブクラス)の他のインスタンスをレシーバとして呼び出すことができます。
    • オブジェクト同士を比較するメソッドなどで使われます。
ファイルを編集:access_control_demo.rb
class Wallet
  attr_reader :id

  def initialize(id, amount)
    @id = id
    @balance = amount # private なインスタンス変数
  end

  # public メソッド (外部インターフェース)
  def transfer(other_wallet, amount)
    if withdraw(amount)
      other_wallet.deposit(amount)
      puts "Transferred #{amount} from #{self.id} to #{other_wallet.id}"
    else
      puts "Transfer failed: Insufficient funds in #{self.id}"
    end
  end

  # protected メソッド (インスタンス間での連携)
  protected

  def deposit(amount)
    @balance += amount
  end

  # private メソッド (内部処理)
  private

  def withdraw(amount)
    if @balance >= amount
      @balance -= amount
      true
    else
      false
    end
  end
end

w1 = Wallet.new("Wallet-A", 100)
w2 = Wallet.new("Wallet-B", 50)

# public メソッドはどこからでも呼べる
w1.transfer(w2, 70)

puts "w1 ID: #{w1.id}"
# puts "w1 Balance: #{w1.balance}" #=> NoMethodError (attr_reader がないため)

# private / protected メソッドは外部から直接呼べない
# w1.deposit(100)  #=> NoMethodError: protected method `deposit' called...
# w1.withdraw(10)  #=> NoMethodError: private method `withdraw' called...
ruby access_control_demo.rb
ブラウザ上で動作するRuby3.4の実行環境です。
左上の実行ボタンを押して、このページ内のaccess_control_demo.rbに書かれている内容を実行します。
Transferred 70 from Wallet-A to Wallet-B
w1 ID: Wallet-A

この例では、transfer (public) が内部で withdraw (private) を呼び出し、引数で受け取った other_wallet の deposit (protected) を呼び出しています。deposit は protected なので、other_wallet. というレシーバを明示しても Wallet クラス内からは呼び出せます。

この章のまとめ

  • モジュールは module キーワードで定義され、名前空間とミックスインの2つの役割を持ちます。
  • 名前空間としては、:: を使って定数やクラスをグループ化し、名前の衝突を防ぎます。
  • ミックスインとしては、include することでモジュールのメソッドをインスタンスメソッドとしてクラスに追加できます。これは多重継承の代わりとなる強力な機能です。
  • extend は、モジュールのメソッドをクラスメソッドとして追加します。
  • public, private, protected でメソッドの可視性を制御します。
  • Rubyの private は「レシーバを指定して呼び出せない」というユニークな制約を持ちます。

練習問題1: カウンター機能のミックスイン

Enumerable モジュール(第6章で少し触れました)のように、include したクラスに便利な機能を追加するモジュールを作成します。

  1. Counter というモジュールを定義してください。
  2. Counter モジュールは count_items(item_to_find) というメソッドを持つものとします。
  3. このメソッドは、include したクラスが items という名前の配列(Array)を返すインスタンスメソッドを持っていることを前提とします。
  4. count_items は、その items 配列内に item_to_find がいくつ含まれているかを返します。
  5. ShoppingCart クラスと WordList クラスを作成し、両方で items メソッドを実装し、Counter モジュールを include して count_items が動作することを確認してください。
ファイルを編集:practice8_1.rb
module Counter

end

class ShoppingCart

end

class WordList

end


ruby practice8_1.rb
ブラウザ上で動作するRuby3.4の実行環境です。
左上の実行ボタンを押して、このページ内のpractice8_1.rbに書かれている内容を実行します。

練習問題2: protected を使った比較

protected のユースケースを理解するための問題です。

  1. Score クラスを作成します。initialize で @value (得点)をインスタンス変数として保持します。
  2. higher_than?(other_score) という public なインスタンスメソッドを定義してください。これは、other_score (Score の別のインスタンス)より自分の @value が高ければ true を返します。
  3. higher_than? メソッドの実装のために、value という protected メソッドを作成し、@value を返すようにしてください。
  4. higher_than? の内部では、self.value > other_score.value のように protected メソッドを呼び出してください。
  5. 2つの Score インスタンスを作成し、higher_than? が正しく動作することを確認してください。また、protected メソッドである value をインスタンスの外部から直接呼び出そうとするとエラーになることも示してください。
ファイルを編集:practice8_2.rb
class Score

end

ruby practice8_2.rb
ブラウザ上で動作するRuby3.4の実行環境です。
左上の実行ボタンを押して、このページ内のpractice8_2.rbに書かれている内容を実行します。
前のページ« クラスとオブジェクト
次のページProc, Lambda, クロージャ »