オブジェクトについて学習してきたがここでクラスとモジュールの概念について学ぶ。
クラス はオブジェクトになれる、、、つまりインスタンス化能力を持ち、インスタンスメソッドやクラスメソッドを格納できる。
モジュール はインスタンス化能力は持たないがメソッドを格納できる。
例えば、以下のクラスとモジュールがあるとする。
module Panda
def visit_to_Japan
"Mr.TonTon"
end
def panda
@panda = "panda"
end
end
class Zoo
def the_zoo
"There are lots of animal"
end
end
class UenoZoo < Zoo
include Panda
def monkey
@monkey = "monkey"
end
def elephant
@elephant = "elephant"
end
def lion
@lion = "lion"
end
def self.name
"Ueno Zoo"
end
end
モジュールをクラスに取り込む事をmixinといい、ソフトクリームの上に載せるトッピングがその名の由来。
Rubyのクラスは継承ができ、親のクラスのインスタンスメソッドを子が受け継ぐ。
1つのクラスに二つ以上のクラスは継承ができなく、これを 単純継承 という。
上記のコードはUenoZooクラスはZooクラスのメソッドを継承している。またUenoZooクラスはPandaモジュールをmixinしている。
Zooクラスで定義されているインスタンスメソッドはUenoZooクラスで使えて、Pandaモジュールをインクルードすることによって、Zooクラスでインスタンスメソッドとして使う事ができる。
Moduleはクラスの使い方にバリエーションを与える。もしModuleをクラスメソッドとして定義したい場合は extend を使用する事によって使う事ができる。
class UenoZoo < Zoo
extend Panda
def monkey
@monkey = "monkey"
end
def elephant
@elephant = "elephant"
end
def lion
@lion = "lion"
end
def self.name
"Ueno Zoo"
end
end
> UenoZoo.visit_to_Japan
=> "Mr.TonTon"
また prepend を使えば継承関係を一番手前にしてモジュールにあるメソッドにsuperを使ってオーバーライド(メソッド上書き)できるようになる、Railsのコントローラーで使われるbefore actionのような機能を実装できる。
super メソッドは継承されているクラスのメソッド、もしくはモジュールでmixinされたメソッドで同名のメソッドを呼ぶ事ができるメソッド。
module Panda
def visit_to_Japan
super + " and " + "Mr.TonTon"
end
def panda
@panda = "panda"
end
end
class UenoZoo < Zoo
prepend Panda
def monkey
@monkey = "monkey"
end
def elephant
@elephant = "elephant"
end
def lion
@lion = "lion"
end
def self.name
"Ueno Zoo"
end
end
> UenoZoo.ancestors
=> [Panda, UenoZoo, Zoo, Object, Kernel, BasicObject]
# Panda モジュールが手前にくる。includeだと
# => [UenoZoo, Panda, Zoo, Object, Kernel, BasicObject]
# Panda モジュールが後ろにくる。
UenoZoo.new.visit_to_Japan
=> "Mr.RanRan and Mr.TonTon"
ちなみにクラスにはinitializeメソッドという便利なメソッドが定義されている。
このメソッドはオブジェクトが作成された時に呼ばれるメソッドである。
例えば、上のコードでmonkeyメソッドとelephantメソッドとlionメソッドはインスタンス変数を定義してメソッドに格納しているが下記のメソッドのようにすれば短くなる。
class UenoZoo
def initialize(monkey="monkey", elephant="elephant", lion="lion")
@monkey = monkey
@elephant = elephant
@lion = lion
end
end
UenoZoo.new.instance_variables
=> [:@monkey, :@elephant, :@lion]
このままだとメソッドで変数を呼び出せないので, attr_readerを使う。
class UenoZoo
attr_reader :monkey, :elephant, :lion
def initialize(monkey="monkey", elephant="elephant", lion="lion")
@monkey = monkey
@elephant = elephant
@lion = lion
end
end
UenoZoo.new.monkey
=> "monkey"
Ueno.new.monkey = "pokey"
=> undefined method `monkey=' for #<UenoZoo:0x007fa782157008> (NoMethodError)
ただこのメソッドは格納したインスタンス変数を呼び出す機能しかないので、書き込み機能も加えるためにattr_accessorを使う。
書き込みのみの場合はattr_wiriterを使う。
class UenoZoo
attr_accessor :monkey, :elephant, :lion
def initialize(monkey="monkey", elephant="elephant", lion="lion")
@monkey = monkey
@elephant = elephant
@lion = lion
end
end
UenoZoo.new.monkey
=> "monkey"
Ueno.new.monkey = "pokey"
=> "pokey"
モジュールとしての使い方は2種類あって、メソッドの格納庫として使う場合か名前空間として使う場合か。
名前空間としての使い方とはRailsを使っている場合だと背後にたくさんのライブラリがあり同じクラスを定義して、既存のメソッドとは知らずに新たに定義し直して、将来的なバグに繋がる可能性になる。またクラス名とモジュール名は定数で定義されていて、module名で定義された定数はclass名で定義できない。
その場合は名前空間として使う。下記のコードを参照してほしい。
class Cat
def tuna
"delicious"
end
end
module Zoo
class Cat
def tuna
"delicious"
end
end
end
> Zoo::Cat.new.tuna
=> "delicious"
> Cat.new.tuna
=> "delicious"
CatクラスとZoo::Catクラスは別々のオブジェクトになる。
module名とclass名は定数だが定数の中から定数を呼ぶ場合はコロンを二個つけて::で呼び出す。
##まとめ
###クラスはインスタンス化能力をもちメソッドを定義できる、モジュールはインスタンス化能力を持たないがメソッドを格納できる。モジュールの利用法はメソッドを格納するか名前空間として利用する。
###クラスにモジュールを取り込むことをmixinといってincludeを使う。
###またクラスメソッドとして取り込む場合はextend、継承関係に着目して取り込む場合はprepend。attr属性(attr_accessor, attr_writer, attr_reader)を使えば、そのクラスに属性を与えられる。initializeメソッドを使えば、オブジェクトが生成された時に値を格納したりする事ができる。
[続く、、、、]
(http://qiita.com/ToruFukui/items/be29968da6dc9d125315)