Rubyを用いてデザインパターンを学ぶ
デザインパターンとは
Gang Of Four(GoF)が定義したもの Javaで書かれた本がある。 「オブジェクト指向における再利用のためのデザインパターン」 1999/10 出版
- プログラミングにおいて繰り返し現れる問題に対する、適切解のパターン
- 無駄なく設計されたオブジェクト指向プログラムの実現をサポート
- 諸刃の剣
パターンとしてカタログ化されていることで 車輪の再発明を防ぐ
デザインパターンに重要な5つの考え
- 変わるものを変わらないものから分離する
- プログラムはインターフェースに対して行う (実装に対しては行わない)
- 継承より集約
- 委譲、委譲、委譲
- 必要になるまで作るな (YAGNI)
変わるものを変わらないものから分離する
理想的なシステムは、すべての変更が局所的であるべき。 ソフトウェアに完璧は存在しない。 変わるものを変わらないものから分離することによって、 将来起こりうる「新たな変更」に対して柔軟に対応できるようにしておく。
プログラムはインターフェースに対して行う (実装に対しては行わない)
可能な限り「一般的・抽象的なもの」に対してプログラミングすること。 (ここで言うインターフェイスは、Javaの組み込み構文としてのインターフェイスではなく、 より広いレベルで、「抽象度を高めたもの」を意味する。)
Bad
具象性が高く、密結合なコード
if is_car my_car = Car.new my_car.drive(200) else my_car = get_vehicle my_vehicle.travel(200) end
Good
抽象度が高く、疎結合なコード
my_vehicle = get_vehicle
my_vehicle.travel(200)
抽象度を高くすることによりコードにより変更に強くなる
継承より集約
継承はよろしくない繋がりをつくってしまう
class Vehicle def start_engine # エンジンスタートの処理 end def stop_engine # エンジンストップの処理 end end class Car < Vehicle def drive(distance) start_engine() # driving.... stop_engine() end end
- Carからエンジンの実装が丸見え
- エンジンを使用しない乗り物を作りたい場合には大きな変更を加える必要が出てくる
-> 変わりやすい部分(Engine)を変わりにくい部分(Vehicle)から分離できていない
じゃあどうするのか?
集約を使い解決する。 オブジェクトに「他のオブジェクトに対する参照」を持たせる。 オブジェクトが何かの一種である(is-a-kind-of)関係(継承)を避け、 何かを持っている(has-a)関係(集約)にする
依存オブジェクトの注入
class Engine def start # エンジンスタートの処理 end def stop # エンジンストップの処理 end end class Car def initialize @engine = Engine.new() # 依存オブジェクトの注入 end def drive(distance) @engine.start() # distanceだけ走る処理 @engine.stop() end end
これでEngineをVehicleから分離し、カプセル化された 依存オブジェクトの取り扱いには注意する必要がある。
委譲、委譲、委譲
委譲(delegation)
class Car def initialize @engine = GasolineEngine.new end def drive @engine.start # driving.. @engine.stop end def switch_to_diesel @engine = DieselEngine.new end def start_engine @eigine.start # Engineクラスに任せる end def stop_engine @eigine.stop # Engineクラスに任せる end end
集約と委譲の組み合わせは、強力かつ柔軟な継承代替手段となる。
必要になるまで作るな (YAGNI)
You Ain't Gonna Need It
「将来使うかも」は、大抵は使わない
これはデザインパターンに限った話ではなくアプリケーション開発全般においても言えること。 不要な処理は書かない。 必要になった時に必要な分だけの実装を行い無駄のないシステムを作る。