質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.35%

  • Ruby

    10222questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Moduleとは

受付中

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 552

akamakku

score 185

クラスは分かるんですが、モジュールがわかりません。

クラスは、フィールドとメソッドをひとまとめにした、オブジェクトの設計図。

モジュールは、メソッドをひとまとめにした工具箱。

的な認識でいいんでしょうか?

またモジュールはどんな場面で使いますか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+2

モジュールとクラスの違いは主に二つだけです。

  1. クラスはインスタンスを作れるが、モジュールは作れない。
  2. モジュールはインクルードできるが、クラスはできない。

クラスもモジュールも一つのオブジェジェクトです。クラスのクラスであるClassはモジュールのクラスであるModuleを継承しています。上記の2.に関わる一部のメソッドを除き(上書きで未定義されている)、モジュールでできることはクラスでもできます。逆に、1.に関わる機能はクラスにしか実装されていません。

なら、クラスに2.の機能を持たせればクラスだけで良かったじゃん…とはなりません。インスタンスにできないからこそモジュールは役に立つ機能を備えることができるのです。モジュールの利用方法は主に三つです。

  • mixin
  • ユーティリティクラス
  • 名前空間

mixin

オブジェクト指向においてクラスの継承は重要な概念の一つです。静的型付き言語ではポリモーフィズムの実現という意味でも重要ですが、ダックタイピングが基本となるRubyのような動的型付き言語では、もっぱらコードを共通化すること、つまり、DRYの実現という面で重要になります。

さて、ここで、Aクラス、Bクラス、Cクラスを考えましょう。

class A
  def hoge
    ...
  end
  def fuga
    ...
  end
end

class B
  def hoge
    ...
  end
end

class C
  def fuga
    ...
  end
end

各メソッドは内容が同じだったとします。メソッドhogeはAクラスとBクラスで共通です。DRYに則すのであれば、スーパークラスとしてXクラスを作ってまとめることができるでしょう。同じようにメソッドfugaはAとCで共通ですので、Yクラスを作ってまとめることができるでしょう。

class X
  def hoge
    ...
  end
end

class Y
  def fuga
    ...
  end
end

class A < X, Y
end

class B < X
end

class C < Y
end

これを多重継承というのですが、Rubyではできません(上のコードはRubyとしては間違っています)。多重継承には菱形継承問題という単純に解決できない難しい問題があるためです(解決策が全くないと言うことではなく、現にC++やPythonは多重継承を採用しています)。Rubyを含む多くの言語ではこの問題を避けるために多重継承を禁止し、単一継承のみにしました。

単一継承しかないと上のコードのようにYクラスでまとめると言うことができません。そこで用意されている機能がmixinです。AクラスとCクラスの共通部分はZモジュールとしてまとめて、インクルードすることができます。

class X
  def hoge
    ...
  end
end

module Z
  def fuga
    ...
  end
end

class A < X
  include Z
end

class B < X
end

class C
  include Z
end

こうして単一継承であってもDRYを守ることができました。クラスとして共通化するかモジュールとして共通化するかは場合によりけりなので単純ではありませんが、単一継承とmixinを使うことで多重継承特有の問題を回避することができました。

これがどのように役に立つのか、具体的にどんなところで使われているかをみるとわかりやすいでしょう。Enumerableモジュールは繰り返しを行うときに役に立つメソッドを追加する物です。何かしらを繰り返す(each)ようなオブジェクトのクラスを作ったとしましょう。そうなると、繰り返しで現れた物をまとめて配列にするメソッド(to_a)、繰り返しから条件に合う要素を探すメソッド(find)、繰り返しの中で最大値を取り出すメソッド(max)等も欲しくなるかもしれません。それらを一々全部実装していたらとても大変です。しかし、Enumerableモジュールをインクルードすると、eachメソッドさえ実装してあれば、それらの繰り返しによく使うようなメソッド類が自動的に使えるようになります。eachメソッドをつかって配列にするto_aメソッドを自分で実装する必要がなくなると言うことです。他にはComparableモジュールが同じようにmixinで使う代表例です。

【備考】
mixinに非常に似た機能にトレイトがあります。mixinが継承を利用しているのに対し、トレイトはクラスそのものへ注入する形です。厳密には似て非なる機能ですが、実現したいことはほとんど同じです。

ユーティリティクラス

インスタンスとは関連づけられないような定数やメソッドを作りたいときがあります。代表的なのは数学の分野で使われるような円周率や三角関数でしょう。円周率のような定数はどのインスタンスとも結びつけられません。三角関数はどうかというと、たしかに、1.sinみたいな実装はできないことはないですが、あれもこれも数値のクラスに足して行ったら切りがありません。それに数値と言っても、整数、浮動小数点数、有理数、複素数など種類があり、それぞれにそのようなメソッドを追加することも大変です。

そこで、インスタンスを作らなくても使える定数やメソッド類をまとめたモジュールを作れば便利になります。これをユーティリティクラスと言います。モジュールなのにユーティリティクラス?と思ったかも知れませんが、この用語自体はRubyだけの用語というわけでは無く、他の言語ではクラスを使って実装していることが多くあったためこのような名前が付いています。

モジュールではクラスの特異メソッド(クラスメソッドと言われる)と同じく、インスタンスの生成なしで直接呼び出せるモジュールの特異メソッドが実装できます。さらに便利なのは、module_functionを使うと、インスタンスメソッド兼モジュールの特異メソッド(この二つが定義されているようなメソッドをモジュール関数という)になると言うことです。こうすることで、通常のクラスメソッドのようにZ.fugaと呼び出すこともできますし、インクルードしたクラス内などではfugaだけで呼び出すことも可能になります。

ユーティリティクラスの代表例はMathモジュールでしょう。円周率の定数Math::PIや正弦関数Math.#sinが定義されています。

【備考】
ユーティリティクラスの実装方法は言語によって大きく異なります。Rubyではモジュールを使うと言うだけで、他の言語では全く違う場合があります。また、ユーティリティクラスについて全否定するような記事が見受けられますが、Mathのようなものをどのように実装すべきかまで書いている記事は見つけられませんでした。もし、ご存じの方はコメントいただけると助かります。

名前空間

プログラムが巨大になるにつれて名前が重複してしまう事があります。メソッド名はクラスやモジュール内ですのでまだ良いのですが、クラス名やモジュール名などが重なるのは非常に不味いです。また、外部ライブラリを利用するとなると、ライブラリ内部で使っている全ての名前まで気にしなくてはいけません。

そこで考えられたのが名前空間です。機能などに基づき、適当な名前を頭に付けるようにすることで、同じクラス名であっても問題ないようにします。外部ライブラリもライブラリ名が最初に付いていれば、重複することも無くなるでしょう。Cのような名前空間が言語として実装されていない場合は、一つ一つの関数にプリフィックスを付けておくことで名前空間は実現できますが、これはとても面倒です。そこで、他の言語では名前空間を実現するための機能を持ち、外部からはプリフィックス有りでしかアクセスできず、内部ではプリフィックス無しでアクセスするようにしました。プリフィックスと言ってもスコープがどこかということを区別するための物で、「名前空間の名前 + スコープ解決演算子(多くは::または.を用いる)」になります。

Rubyではこの名前空間の実現にモジュールを使います。実際はクラスでも可能ですが、インスタンスを作る必要が全くないためモジュールを使うという手段が用いられます。

組み込みライブラリでこのような名前空間として使っている物はErrnoモジュールです。このモジュールにはメソッドが何も定義されていません。システムコール系の例外Errno::EACCES等をまとめるためだけに使われています。標準ライブラリやGem等で配付されているライブラリになるとさらに多くの例が見つかるでしょう。例えばnet/httpnet/smtpはNetモジュールの下にクラスを作りますが、Netモジュール自体は特に何もしません。単に名前空間を提供しているだけです。


モジュールは上の機能の一つしか使ってはいけないというわけではありません。実際は名前空間を兼ねることは多くありますし、ユーティリティクラスはそもそもmixinして使えるようになっている事が普通です。また、名前空間やユーティリティクラスのような使い方はクラスでも実現はできます。しかし、mixinできるのはモジュールだけです。インスタンス化はできないという欠点はありますが、mixinはとても強力な機能であるため、そもそもインスタンス化の必要性が無ければモジュールとして作っておいた方が良いのかも知れません。

最後にRubyにはフィールドやメンバ変数という概念はありません。インスタンス変数やクラス変数および定数は似て非なる物です(ここら辺はオブジェクト指向そのものに対する設計思想の違いによるもののため、説明するとかなり長くなるので省きます)。似たようなことができるという意味では同じように思うかも知れませんが、完全に同じだと思うと些細な違いで訳がわからなくなるので注意してください。なお、mixinするモジュールの中のメソッド定義でもインスタンス変数もクラス変数も使えます。もちろん、モジュール内で定数も定義できます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Ruby

    10222questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。