Moduleのミックスインと名前空間に関して [Ruby]
Rubyのチェリー本を参考に、Rubyの基本的な部分の理解を深めたいと思います。中でも今回はモジュールについて記載します。
Module の概要
継承を使わずにクラスにメソッドの追加・上書きできる (= ミックスイン)
名前空間を作る
クラスの継承は、「is-a の関係」に基づいています。継承関係ではないが、共通のメソッドを利用できるようにしたい。そんなときに選択肢として挙がるのがモジュールです。
ちなみにモジュールのクラスと異なる特徴は以下の通りです
インスタンスを作成できない
他のモジュールやクラスを継承できない
ミックスイン: クラスにメソッドの追加・上書き
結論 / サンプルコード
モジュールの役割の1つ、ミックスインとは、いくつかのクラスに共通のメソッド・定数を利用可能にすることです。ミックスインはモジュールをモジュールに組み込むこともできます。
# モジュールを作成
module Hello
def hello_world
puts "Hello world!!"
end
end
class User
# 上で作ったモジュールをミックスインする
include Hello
end
user=User.new
user.hello_world
#=> Hello world!!
また1つのクラスに複数のモジュールをミックスインすることもできるます。これによりRubyではミックスインを利用することで多重継承に似たしくみを実現することできます。
include module_name
モジュールにメソッドを定義し、そのモジュールをクラスでincludeすると、モジュールで定義したメソッドがインスタンスメソッドとして利用可能になります。
# モジュールをミックスイン (include)
include Hello
user=User.new
user.hello_world
#=> Hello world!!
モジュール内でメソッドを private メソッドにした場合は、ミックスイン先でも private メソッドとして扱われます。
またincludeと似た機能として、prependがあります。prependの場合、モジュールとミックスイン先のクラスに同名のメソッドがあった場合には、prependされたモジュール内のメソッドが優先されるようになります。
extend module_name
モジュールをincludeではなくextend を用いてミックスインした場合、そのクラスの特異メソッド (=クラスメソッド) として扱われます。
# モジュールをミックスイン (extend)
extend Hello
User.hello_world
#=> Hello world!!
名前空間
結論 / サンプルコード
モジュールのもう2つ目の機能は名前空間です。モジュール構文の中にクラス定義を書くと「そのモジュールに属するクラス」という意味になり、名前の衝突を防ぐことができます。
# モジュール配下にクラスを定義
Module Baseball
Class Second
def initialize
puts 'Baseballモジュールの中の、Secondクラスのインスタンスが作成されました'
end
end
end
# 該当クラスは、"モジュール名::クラス名" として認識される
Baseball::Second.new
#=> Baseballモジュールの中の、Secondクラスのインスタンスが作成されました
名前空間のその他
既に定義されているBaseballモジュールを名前空間として使いたい場合は、以下のように記述することでクラスを該当の名前空間内に定義できます。
# 既に定義済み
module Baseball
:
end
# モジュール::クラス名の形でクラスを定義
Class Baseball::Second
:
end
モジュールのその他
代表的な Ruby が提供する標準 モジュール
Rubyでは、公式で定義されているモジュールがいくつか存在します。これはRuby が動的型付け言語であるため、「メソッドを実行する瞬間にそのメソッドが呼び出せれば良い」という考え方に基づいています。(ここではその説明は省略)。
以下にそれらの紹介を、主にチェリー本の引用ベースで説明します。
Enumerable モジュール
Array や Hash といったクラスには標準で組み込まれており、以下はその代表的なメソッドです。
map, select, find, countなど
# Arrayクラスには既にinclude済み
Array.include?(Enumerable) #=> true
Comparable モジュール
これは計算 / 演算子用のメソッドがまとまっているモジュールで、以下はその代表的なメソッドです。
<, <=, ==, >, >=, between?
ただしこのモジュールをincludeして利用する為には、以下の条件を満たす必要があります。
Kernel モジュール
当たり前の様に利用している、以下のメソッド達が定義されているモジュール
puts p print require loop
ただしこのモジュールは自作クラスやモジュールで組み込むことはありません。なぜなら、このモジュールはデフォルトのクラスの継承元、ObjectクラスがKernelモジュールをincludeしているからです。
参考: module Kernel
include?, ancesters
表題の2つのメソッドは引数のオブジェクトに対して、該当の module が組み込まれているか確認するメソッド です。
include? は引数にモジュールを取り、クラスが引数のモジュールをミックスインしているかどうか真偽値を返します。
Object.include?(Kernel)
#=> true
ancesters はクラスやモジュールに対して呼び出し、そのオブジェクトが「継承しているクラス」と「ミックスインしているモジュール」を全て表示します。その際に継承しているクラスがミックインしているモジュールも表示され、返り値の順番は参照する優先順になります。
DVD.ancestors
#=> [DVD,B,A,Product,Object,Kernel,BasicObject]
module のその他の機能
Rubyのチェリー本には、モジュールの4つの機能を紹介していました。残りの2つの機能に関しては以下になりますが、ここでは説明を省かせて頂きます。
関数や定数を提供するモジュール ( ≒ シングルトンパターン)
状態を保持するモジュールの作成 ( ≒ 定数管理)
ちなみに上記の2つ機能は、モジュールがインスタンスを作成する必要がない (できない)、というメリットを活かした機能になっています。
モジュールと require メソッドの違い
調べていく内に、以下の疑問を抱きました。
「include」と「require」って何が違う?
気になったので調べた結果、あくまでrequireメソッドは引数のファイルを読み込むためのメソッドだそうです。なので読み込んだだけでは、特に変化はありません。例えばその読み込んだファイルがモジュールを定義したファイルだった場合、そのモジュールを include module_name で読み込まない限りはミックスインされない、という感じの違いです。(恐らく)
reauire 'app/dir1/dir2/file_name'
おわり
前回の記事に引き続き、RailsではなくRubyの基礎的なメソッドの理解を深める目的の記事でした!
今回のモジュールに関しても、よく見かけるにも関わらず全然理解できていないものでしたので、一度立ち戻って理解を深めることができてよかったです!
最後までお読み頂きありがとうございましたー ^_^
参考
https://www.amazon.co.jp/プロを目指す人のためのRuby入門-言語仕様からテスト駆動開発・デバッグ技法まで-Software-Design-plusシリーズ/dp/4774193976
http://blog.livedoor.jp/sasata299/archives/51268600.html
この記事が気に入ったらサポートをしてみませんか?