ピッケル本を読む(2)第3章 クラス、オブジェクト、変数
ジュークボックスに入れる曲をクラスと想定して説明を進めるらしい。Songクラスなどの曲を表すクラスを書いて動作を確認しながら進む。
メモ
- newするとinitializeメソッドが呼び出される(オブジェクトの初期化に使う)
- メソッドの引数はメソッド内のローカル変数と同じ様に動作する
class Song def initialize(name, artist, duration) @name = name @artist = artist @duration = duration end end song = Song.new("Bicylops", "Fleck", 260) p song.inspect #=> "#<Song:0x7fdbb7d8 @duration=260, @name=\"Bicylops\", @artist=\"Fleck\">" p song.to_s #=> "#<Song:0x1002f96c>"
- Rubyでは、クラス定義は固定されず、動的に変更可能
- 従って、既存のクラスにいつでもメソッドを追加できる
- ためしに、to_sメソッドをオーバーライドしてみる(クラス内で既に書いた部分は再度書かないが、実際は存在する)
class Song def to_s "Song: #@name--#@artist (#@duration)" end end song = Song.new("Bicylops", "Fleck", 260) p song.to_s #=> "Song: Bicylops--Fleck (260)"
- Rubyで継承はこう書く
# class SubClass < SuperClass class KaraokeSong < Song def initialize(name, artist, duration, lyrics) # superの呼び出し元メソッド(ここではititialize)と同じ名前のメソッドを親クラスから呼び出す super(name, artist, duration, lyrics) @lyrics = lyrics end end song = KaraokeSong.new("My way", "sinatra", 225, "And now, the...") # 自分のクラスにメソッドが実装されてなければ、次々と親クラスを探しに行く # ここでは、Songクラスのto_sメソッドが呼び出される p song.to_s #=> "Song: My way--sinatra (225)"
- 情報分離の考え方
- 子クラスでは親クラスの内部状態を参照しない
保守性の良い例(親クラスと子クラスの情報分離が出来ている)
# 親クラスのメソッドto_sをオーバーライドして、KaraokeSongクラスで歌詞(lyrics)を表示できるようにする class KaraokeSong < Song def to_s # 親クラスの実装に依存しない super + " [#@lyrics]" end end song = KaraokeSong.new("My way", "sinatra", 225, "And now, the...") p song.to_s #=> "Song: My way--sinatra (225) [And now, the...]"
悪い例(実行結果は同じだが、保守性が悪い)
# 親クラスのメソッドto_sをオーバーライドして、KaraokeSongクラスで歌詞(lyrics)を表示できるようにする class KaraokeSong < Song def to_s # 親クラスのインスタンス変数を直接参照している "KaraokeSong: #@name--#@artist (#@duration) [#@lyrics]" end end song = KaraokeSong.new("My way", "sinatra", 225, "And now, the...") p song.to_s #=> "Song: My way--sinatra (225) [And now, the...]"
- クラス定義のときに何も指定しないと Object クラスが暗黙に指定される
- 全てのオブジェクトは Object を祖先に持つ
- to_sは Object クラスのインスタンスメソッドなので、全てのオブジェクトに対して送信できる
- Rubyは単一継承のシンプルさと多重継承の強力さを併せ持つらしい
- Rubyは単一継承言語
- しかし、任意の数のmixinの機能を取り込む事によって、トラブル無く多重継承と同等の機能を利用できる
- 結局、mixinって何なんだ?大分先で詳しく説明するらしい
- オブジェクトと属性
- 属性=オブジェクトの外部から見える側面(APIのことかな?)
- Rubyのgetterとsetter(アクセサメソッド)は簡単に、保守性良く書ける
- :artistという構文は、artistに対応するSymbolオブジェクト(変数artistの名前)を返す式
- コロン無しのartistは変数artistの値を意味する
保守性の良いゲッター
clsss Song # Rubyではこの種のアクセサメソッドが用意されてる # ゲッター attr_reader :name, :artist, :duration end song = Song.new("Bicylops", "Fleck", 260) song.artist #=> "Fleck" song.name #=> "Bicylops" song.duration #=> 260
保守性の悪いゲッター(ゲッターの追加と削除の管理が大変)
clsss Song # 普通、getterは次のように書けるが、保守性が悪い def name @name end def artist @artist end def duration @duration end end song = Song.new("Bicylops", "Fleck", 260) song.artist #=> "Fleck" song.name #=> "Bicylops" song.duration #=> 260
保守性の良いセッター
clsss Song # Rubyではこの種のアクセサメソッドが用意されてる # セッター attr_writer :duration end song = Song.new("Bicylops", "Fleck", 260) song.duration = 257 song.duration #=> 257
保守性の悪いセッター(セッターの追加と削除の管理が大変)
clsss Song # セッター # Rubyでは、等号で終わるメソッドを書くことで、自然に値を代入するように出来る def duration=(new_duration) @duration = new_duration end end song = Song.new("Bicylops", "Fleck", 260) song.duration = 257 song.duration #=> 257
- 仮想属性
class Song # 仮想属性(外部からは普通の属性と同じに見える) def duration_in_minutes @duration / 60.0 # 浮動小数点に強制的に変換 end def duration_in_minutes=(new_duration) @duration = (new_duration*60).to_i end end song = Song.new("Bicylops", "Fleck", 260) p song.duration_in_minutes #=> 4.33333333333333 # 一見、普通の属性を介して代入しているように見えるが、実際は仮想属性を使ってる song.duration_in_minutes = 4.2 p song.duration #=> 252
- 属性と普通のメソッドの違い
- クラス変数
- クラスメソッド
- メソッド名の前にクラス名とピリオドを付けて定義する
- 特定のオブジェクトに関連付けられないメソッドのこと
- newメソッドもクラスメソッドらしい
class Example def instance_method # インスタンスメソッド end def Example.class_method # クラスメソッド end end
- シングルトンの構文
- オブジェクトの実体を一つしか作れないようにするデザインパターンらしい
- クラスメソッドの使い方の良い具体例だ
# MyLogger.createを呼び出すことでしかログ記録オブジェクトが生成されないようにする # また1つしかログ記録オブジェクトが生成されないようにする class MyLogger private_class_method :new @@logger = nil def MyLogger.create @@logger = new unless @@logger @@logger end end
- アクセス制御
- public:アクセス制御は行われないデフォルトのメソッド保護方式
- protected:定義元クラスおよびその子クラスのオブジェクトだけが呼び出せる
- private:明示的なレシーバを指定して呼び出すことは出来ず、レシーバは常に自分自身(self)
- Rubyのアクセス制御は、プログラム実行時に動的に決定されるらしい
- 変数
- 変数は単にオブジェクトへのリファレンスに過ぎない
person1 = "Tim" person2 = person1 person1[0] = 'J' # person1とperson2は同じオブジェクトを指してる p person1 #=> "Jim" p person2 #=> "Jim"
person1 = "Tim" # Stringのdupメソッドで、同じ中身の新しいStringオブジェクトが作成される person2 = person1.dup person1[0] = 'J' # person1とperson2は違うオブジェクトを指してる p person1 #=> "Jim" p person2 #=> "Tim"
まとめ
クラス、オブジェクト、変数の詳細について勉強した。シングルトンが簡単に実装できるのが面白いと思った。何かを一元管理したいときに便利な書き方なのかも知れない。