プログラムスクールで勉強しているときにはほとんど出てこなかったが、名前だけでも聞くことがあった「モジュール」について気になったので、例のごとく「プロを目指す人ためのRuby入門」で調べてみました。
Contents
モジュールについて
特徴
モジュールの特徴は大きく2つ
①クラスと違って、モジュールからインスタンスは作れない
②他のモジュールやクラスを継承することはできない。
⇒継承の場合、Rubyは単一継承なので、複数のクラスを継承することはできないが、モジュールの場合は複数のモジュールをクラスに入れることができる。
①②からモジュールはクラスとは別物と思った方が良い。クラスはよく「データの設計図」みたいなことを言われるが、モジュールは「追加したい機能や共通化させたい機能をパッケージ化」したものという言い方が良いかもしれない。
クラスについてはこちらで内容をまとめています
モジュールの使用用途はいくつかあります。ここでは3つを紹介したいと思います。
①既存のクラスに機能を追加する
②クラス名の重複を防ぐ(名前空間の作成)
③モジュール単体で使う
①既存のクラスに機能を追加する
既存のクラスに機能を追加することを「ミックスイン」と言う。ミックスインする方法はinclude、extend、prependの3つがある。
include
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
module Name def name puts "chopesu" end end class Hello include Name #includeでNameモジュールをミックスイン def hello name 'Hello.' #Nameモジュールのnameメソッドを呼び出す end end name = Name.new name.hello #chopesu Hello. |
extend
extendはincludeとは違って、モジュール内のメソッドをミックスイン先のクラスの特異メソッド(クラスメソッド)にすることができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module Name def name puts "chopesu" end end class Greeting extend Name #extendでNameモジュールをミックスイン def self.hello #nameメソッドをクラスメソッド内で呼び出す。つまり、nameメソッド自体もクラスメソッドの扱いになっている。 name 'Hello.' #Nameモジュールのnameメソッドを呼び出す end end #NameモジュールのnameメソッドGreetingクラスのクラスメソッドとして呼び出すことができる。 Greeting.name #chopesu Greeting.hello #chopesu Hello. |
prepend
prependはRuby2.0から使用できるようになったもので、特徴としては、同名のメソッドがあったときに、ミックスインしたクラスよりも先にモジュールのメソッドが優先して呼ばれることである。この特徴を生かして、既存メソッドの置き換えをすることができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#Productクラスは外部ライブラリで定義されている想定 class Product def name "Avator" end end #変更前のnameメソッドの実行結果は以下の通り product = Product.new product.name #Avator #prependするためのモジュールを用意 module NameChange def name "KINGDOM" end end #既存メソッドを変更するためにProductクラスを再オープンする class Product prepend NameChange #nameメソッドは外部ライブラリで定義されている(Avator)が、prependで同名のnameメソッドが、ミックスインされたので、 #nameメソッドが呼ばれる際は、NameChangeモジュールのnameメソッドが優先して呼ばれる end product = Product.new product.name #KINGDOM |
②クラス名の重複を防ぐ(名前空間の作成)
大規模なプログラムを書いているときに、クラス名が重複する場合が出てくるらしい。そのときにモジュールを使うと、同じクラス名にならないように回避してくれる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module High_school class Student #モジュールの中にクラス定義を記述する def initialize(name, age) @name = name @age = age end end end module University class Student #モジュールの中にクラス定義を記述する def initialize(name, age) @name = name @age = age end end end #高校と大学の生徒をちゃんと区別して作成できる High_school::Student.new('chopesu', 18) University::Student.new('yamada', 20) |
③モジュール単体で使う
モジュールはクラスにミックスインしなくても、単体で使用することができる。そのときに使うのが、「module_function」メソッド。このようにミックス員しても、モジュール単体で特異メソッドとしても使えるメソッドのことを「モジュール関数」と呼ぶ。モジュール関数は、他のクラスにミックスインすると、自動的にprivateメソッドになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
module Name def name puts "chopesu" end #対象メソッドよりも下で、module_functionメソッドを呼び出す。privateと同じ要領 module_function :name end #モジュール単体でメソッドを呼び出すことができる Name.name #chopesu class Greeting include Name #includeでNameモジュールをミックスイン def self.hello name 'Hello.' end end ミックスインとしてnameメソッドを呼び出すこともできる。 Greeting.hello #chopesu Hello. |
refinementsで有効範囲を指定する
Ruby2.0からrefinmentsと呼ばれる機能が導入された。これは、独自の変更の有効範囲(スコープ)を限定することができる。これは例文コードを見た方が分かりやすい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
module StringShuffle #「refine クラス名」で有効範囲を定めたいクラスを指定し、ブロック内で追加したい機能を書く。 refine String do #今回はStringクラスに、文字列の中身をランダムに変えるshuffleメソッドを追加実装 def shuffle chars.shuffle.join end end end 'chopesu'.shuffle #このままだとNoMethodErrorが出る class User #usingでrefinementsを有効化 using StringShuffle def initialize(name) @name = name end def shuffle_name(name) #Userクラス内であれば、Stringクラスのshuffleメソッドが有効になる @name.shuffle end end user = User.new('chopesu') #クラス内でshuffleメソッドが有効になっている user.shuffle_name #ohpcsue |
今回の例では、クラス内でのメソッドの有効範囲を指定したが、Ruby2.4からはモジュール内でもrefineが使えるようになったらしい。