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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

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

Q&A

1回答

1515閲覧

Moduleとは

akamakku

総合スコア191

Ruby

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

0グッド

1クリップ

投稿2016/12/12 02:01

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

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

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

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

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

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

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

###mixin

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

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

Ruby

1class A 2 def hoge 3 ... 4 end 5 def fuga 6 ... 7 end 8end 9 10class B 11 def hoge 12 ... 13 end 14end 15 16class C 17 def fuga 18 ... 19 end 20end

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

Ruby

1class X 2 def hoge 3 ... 4 end 5end 6 7class Y 8 def fuga 9 ... 10 end 11end 12 13class A < X, Y 14end 15 16class B < X 17end 18 19class C < Y 20end

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

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

Ruby

1class X 2 def hoge 3 ... 4 end 5end 6 7module Z 8 def fuga 9 ... 10 end 11end 12 13class A < X 14 include Z 15end 16 17class B < X 18end 19 20class C 21 include Z 22end

こうして単一継承であっても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するモジュールの中のメソッド定義でもインスタンス変数もクラス変数も使えます。もちろん、モジュール内で定数も定義できます。

投稿2016/12/12 21:53

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問