モジュールのミックスイン
モジュールは様々な用途で使われる。主に以下のような使い方がある。
モジュールからはインスタンスを生成できず、他のモジュールやクラスを継承できない。
module Browser def show puts "fire" end end browser = Browser.new #=> NoMethodError: undefined method `new' for Browser:Module module Chrome < Browser end #=> SyntaxError: unexpected '<' module Chrome < Browser ^
ミックスイン(include)
基本、1つのクラスは1つのスーパークラスしか持てない。しかし、is-aの関係になくても複数のクラスで同じような機能が必要になるケースはある。そのような時、モジュールをincludeすると、モジュールで定義したメソッドがインスタンスメソッドとして呼び出せるようになる。継承は使えないが、共通の機能を持たせたい時に使うといい。
module Logger def log puts "this is a log" end end class Car include Logger end class User include Logger end car = Car.new car.log #=> "this is a log" user = User.new user.log #=> "this is a log"
ミックスイン(extend)
モジュールをextendすると、モジュールで定義したメソッドが特異メソッド(クラスメソッド)として呼び出せるようになる。
module Logger def log puts "this is a log" end end class Car extend Logger end class User extend Logger end Car.log #=> "this is a log" User.log #=> "this is a log"
なお、クラス構文直下はselfがクラス自身を意味するので、クラス構文直下でextendした特異メソッドを使える。以下はクラスが読み込まれた瞬間に"good"と表示される。
class Product extend Logger log "good" end #=> "good"
まとめると以下の通り。
- includeするとインスタンスメソッドとして使える
- extendすると特異メソッド(クラスメソッド)として使える
includeされた時にクラスメソッドとインスタンスメソッドを同時に追加する
ひとつのモジュールに特異メソッド(クラスメソッド)とインスタンスメソッドを設定したい時がある。この場合、Loggerモジュールをincludeすると、クラスメソッドとしてlog_of_class_methodが、インスタンスメソッドとしてlog_of_instance_methodが使える。
module Logger def self.included(base) base.extend(ClassMethods) end module ClassMethods def log_of_class_method puts 'This is class method!!' end end def log_of_instance_method puts 'This is instance method!!' end end
モジュールに includedメソッドを定義しておくと、モジュールが include された時に呼び出される。includedの引数は include したクラスやモジュール。それに対して extend すれば、include されたときにクラスメソッドも同時に追加することができる。
Kernelモジュール
Rubyにおける重要なモジュールにKernelモジュールというのがある。このモジュールは以下のようなメソッドを提供している。
puts p print require loop
上のputsように、コンソールで普通に使えるメソッドの多くはKernelモジュールで定義されている。 なぜ、使えるかというと全てのクラスの頂点に立つObjectクラスがKernelモジュールをincludeしてるからである。
[301] pry(main)> Object.include?(Kernel)) => true
クラスの継承関係
BasicObject
↑
Object -> include Kernelモジュール
↑
String, Numeric, Array, Hash....
Rubyでは、Objectクラスが事実上の最高クラスである。ゆえに全てのクラスはKernelモジュールのメソッドを使える。
コンソールで見てみると
[302] pry(main)> self => main [307] pry(main)> self.class => Object
これはmainというObjectクラスのインスタンスが存在していることを意味する。RubyのトップレベルはObjectクラスのmainインスタンスが占めている。
[302] pry(main)> self => main [307] pry(main)> self.class => Object [309] pry(main)> class Member [309] pry(main)* p self [309] pry(main)* end => Member