オブジェクト指向で意識すること
- インスタンス変数はアクセサメソッドで提供するようにする。
- クラスの中で他のクラスをnewするのは出来るだけ避ける。
- 依存関係の逆転
インスタンス変数はアクセサメソッドで提供するようにする。
class Human def initialize(name) @name = name end def name @name end end hoge = Human.new('tarou') puts hoge.name
上のようにアクセサで提供する。いろんな場所でhuman.nameと使われているときに例えばnameメソッドだけ変更 すればよくて済む。rubyならattr_reader使えばよい。
クラスの中で他のクラスをnewするのは出来るだけ避ける。
あるオブジェクトの中でnewするとそのクラスと強い結合が生まれてしまう。 出来ることなら依存オブジェクトの注入(DI)をする。これをすることで注入されたオブジェクトが何であるかには依存しなくなり、注入されたオブジェクトのインターフェースにのみ依存するようになる。ただ、色々理由でDI出来ないこともよくある。その場合は以下のように出来るだけ隔離し依存を明らかにしておく。
class Gear def initialize(chainring, cog, rim, tire) @chainring = chainring @cog = cog @rim = rim @tire = tire end def gear_inches ratio*wheel.diameter end def wheel @wheel ||= Wheel.new(rim, tire) end end
ここからさらにgear_inchesメソッドが複雑化した場合は
def gear_inches #ここがかなり複雑化する ratio*diameter end def diameter @wheel.diameter end
このように外部メッセージを隔離し、依存を明らかにしておく。 この変更により外部メッセージである@wheel.diameterに依存していたgear_inchesは 自身のメソッドであるdiameterに依存するようになり、外部への依存を取り除けた。 仮にwheelクラスがdiameterメソッドの名前を変更したりした場合でも副作用はこのシンプルな ラッパーメソッドだけで済む。この副作用を駆逐するために最初から問題を避ける依存関係逆転というテクがある。
依存関係の逆転
class Gear attr_reader :chainring, :cog def initialize(chainring, cog) @chainring = chainring @cog = cog end def gear_inches(diameter) ratio*diameter end def ratio chainring / cog.to_f end end class Weel attr_reader :rim, :tire, :gear def initialize(rim, tire, chainring, cog) @rim = rim @tire = tire @gear = Gear.new(chainring, cog) end def diameter rim + (tire*2) end def gear_inches gear.gear_inches(diameter) end end
このように依存関係を逆転させる際はより安定したクラスに依存するようにする。 抽象的なクラスまたは、他とあまり結合していないクラスに依存させる。
デメテルの法則
必ずしも守る必要はない。 クラス間の結合を減らすテクニック。 たくさんドットでつなげるとその通り道のクラス全てと結合して再利用が難しいクラスになってしまう。
class LawOfDemeterExample def initialize @another_class = AnotherClass.new end #自身が直接アクセスできるメソッドはOK def sample_method_following_demeter(obj) my_method#オブジェクトO自身のメソッドは呼び出すことができる data1 = @another_class.get_data#呼び出し用のメソッドまたは属性として同じクラス内で宣言されたオブジェクトのメソッドは呼び出すことができる data2 = obj.get_data#メソッド「sample_method_following_demeter」の引数のメソッドは呼び出すことができる end def my_method end end class AnotherClass def get_data end end O = LawOfDemeterExample.new
メソッドsample_method_following_demeterをもつクラスLawOfDemeterExampleのインスタンスをOとすると オブジェクト指向プログラミングにデメテルの法則を適用すると、次のように解釈できる。
- オブジェクトO自身のメソッドは呼び出すことができる
- メソッド「sample_method_following_demeter」の引数のメソッドは呼び出すことができる
- メソッド「sample_method_following_demeter」の内部で生成/初期化したオブジェクトのメソッドは呼び出すことができる
- 呼び出し用のメソッドまたは属性として同じクラス内で宣言されたオブジェクトのメソッドは呼び出すことができる
- オブジェクトOがアクセスできるメソッド「sample_method_following_demeter」のスコープにある全域オブジェクトのメソッドは呼び出すことができる