🦁
Rubyによるデザインパターン~一般的な原則編~
ラスオルセン著の Rubyによるデザインパターン
を読んで整理してみました。
デザインパターンによる一般的な原則
- 変わるものを変わらないものから分離する。
- インターフェースに対してプログラムし、実装に対して行わない。
- 継承より集約
- 委譲、委譲、委譲
- 必要になるまで作るな(YAGNI: You Ain't Gonna Need It)
1. 変わるものを変わらないものから分離する
変わりやすいと思われるものを変わりにくいと思われるものから分離することで、仕様の変更を局所的にすることができる。
2. インターフェースに対してプログラムし、実装に対して行わない
可能な限り一般的(抽象的)なものに対してプログラムすること。
Car
に対してプログラムするのではなく、Vehicle
に対して行う。
抽象度が低く、密結合なコード
if is_car
my_car = Car.new
my_car.drive(200)
else
my_plane = Plane.new
my_plane.fly(200)
end
👉 乗り物が増えるたびにコード全体に変更が必要
抽象度が高く、疎結合なコード
my_vehicle = get_vehicle
my_car.travel(200)
👉 乗り物が増えても変更を加える必要がない
3. 継承より集約
継承を使った場合
継承の場合、サブクラスではスーパークラスのすべての機能を利用することができるが、望ましくないつながりも作成してしまう。
- スーパークラスの振る舞いを変更すれば、サブクラスの振る舞いも変わってしまう。
- スーパークラスの内部の仕組みは注意深く隠蔽しなかったすべてのものがサブクラスから参照可能である。
class Vehicle
def start_engine
end
def stop_engine
end
end
class Car < Vehicle
def sunday_drive
start_engine
~~~
stop_engine
end
end
エンジンを必要としない乗り物(自転車、ヨット)の場合は大改造が必要。
注意を払ってVehicleクラスを作らない限り、エンジンの実装はCarクラスに筒抜けになる。
集約を使った場合
オブジェクトに他のオブジェクトに対する参照を持たせる。
class Engine
def start
end
def stop
end
end
class Car
def initialize
@engine = Engine.new
end
def sunday_drive
@engine.start
~~~
@engine.stop
end
end
-
Car
がEngine
に対してなにかしたい場合は、Engineクラスの公開されたインターフェースを利用する。 - 継承ベースのバージョンではエンジンの実装がサブクラスから参照可能となっていたが、集約ベースではエンジンの詳細がカプセル化された上に、他のエンジンを利用できる可能性も出てきた。
class Car
def initialize
@engine = GasolineEngine.new
end
def sunday_drive
@engine.start
~~~
@engine.stop
end
def switch_to_diesel
@engine = DieselEngine.new
end
end
4. 委譲、委譲、委譲
継承を利用したパターンではstart_engine
メソッドとstop_engine
メソッドを広く公開していた。
集約を利用したパターンでも同じようにクラス外で利用したいこともある。
そのような場合、委譲(delegation)を利用して、Car
側で処理を行わず、Engine
に任せる。
class Car
def initialize
@engine = GasolineEngine.new
end
def sunday_drive
@engine.start
~~~
@engine.stop
end
def switch_to_diesel
@engine = DieselEngine.new
end
def start_engine
@engine.start
end
def stop_engine
@engine.stop
end
end
集約と委譲を利用することは継承に対する強力で柔軟な代替手段
委譲先のオブジェクトに責任転嫁する際に追加のメソッド呼び出しになるため、コストは掛かるが大した手間ではない。
5. 必要になるまで作るな(YAGNI: You Ain't Gonna Need It)
- 今まさに必要としていない機能や設計の柔軟性を実装すべきではない。
👉後で必要にならない可能性も同じようにある。 - デザインパターンは便利なテクニックだが、目的ではない。
👉コードに柔軟性をもたせようとして、理解しにくくした上、普通に書いた場合とさほど変わらないでは本末転倒。
Discussion