📂

Rubyを使ったModuleで名前空間を切る手法と、Classで名前空間を切る手法の違い

2025/01/21に公開

はじめに

Rubyではクラス名の衝突を防ぐために名前空間を切る手法の代表として、

  • moduleを使った名前空間の整理
  • 複数のclassを入れ子にして名前空間を作る手法

この2つがあります。

チェリー本を読み返していて、「そういえばこの2つの手法、なにがどう違うんだっけ?」と疑問をもったので改めて整理してみました。

Moduleを使った名前空間の定義

module AbstractUser
  class User
    def greet
      "Hello!"
    end
  end
end

puts AbstractUser::User.new.greet
#=> Hello!

特徴と利点

  1. インスタンス化ができない
    • moduleの場合、直接インスタンス化できず、純粋な名前空間として機能。
    • 目的:名前空間の整理、メソッドのミックスイン、定数の管理
  2. 役割の分離(関心の分離)
    • moduleは通常、複数のクラスに機能を共有するために使用する。
    • 例えば共通のメソッドや定数を提供するAbstractUserモジュールのような構成
  3. クラスとは異なる機能
    • moduleをmixin(include, extend)して、複数のクラスで再利用可能。

module Greetable
  def greet
    "Hello"
  end
end

class User
  include Greetable
end

puts User.new.greet  # => "Hello"
  1. 多重継承が可能
    • クラスは単一継承だが、moduleは複数のmoduleを取り込むことで、機能を共有することができる
  2. コードの意図が明確
    • moduleを使うことで、「このクラスは名前空間として使用されている」という意図が明示的。

クラスを入れ子にして名前空間を切る手法

class AbstractUser
  class User
    def greet
      "Hello from AbstractUser::User"
    end
  end
end

puts AbstractUser::User.new.greet  # => "Hello from AbstractUser::User"

特徴と利点

  1. インスタンス化が可能
    • moduleと異なり、classはオブジェクトを作成できるため、名前空間としての整理だけでなく、実際に機能をもたせることができる。
  2. カプセル化と組織化
    • class内に論理的に関連するclassをまとめることで、構成を整理しやすい。
    • 例えばAbstractUserに特化したユーザー管理機能をそのスコープ内にもたせる。
  3. アクセス管理の制御
    • クラス内部からは入れ子クラスに直接アクセスできるため、内部実装のカプセル化が可能。
  4. 継承とは無関係
    • Class User < AbstractUserのように継承関係を持たせるのとは異なり、名前空間の管理のみを行う。
  5. 特定の用途で役立つ
    • 例えば関連するclass(subclassではなく)の管理に有用。

違いの比較

項目 Module Class
インスタンス化 不可 可能
用途 名前空間、定数、mixin 名前空間、カプセル化
意図・目的 名前空間、機能の共有、整理 名前空間+クラスの組織化、分類
内部メソッドの利用 include で利用できる 直接定義したり呼び出しが可能
依存関係のカプセル化 不可 可能

まとめ

  • 「名前空間の整理」であればmoduleが適切
  • 「特定のクラスに関連する機能やデータ構造の整理」であればclassを入れ子にする
  • 汎用的に他のclassでも使いまわしたいロジックはmoduleにしてmixinを検討。

Discussion