504
477

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rubyによるデザインパターン5原則

Last updated at Posted at 2014-06-15

概要

改めて基本を学ぶ。
Rubyによるデザインパターン第1章。

デザインパターンとは

  • プログラミングにおいて繰り返し現れる問題に対する、適切解のパターン。
  • 無駄無く設計されたオブジェクト指向プログラムの実現をサポート。

パターンとしてカタログ化されていることで
車輪の再発明を防ぐ

デザインパターンの根底にある5つの考え

  • 変わるものを変わらないものから分離する
  • プログラムはインターフェイスに対して行う(実装に対して行わない)
  • 継承より集約
  • 委譲、委譲、委譲
  • 必要になるまで作るな(YAGNI)

変わるものを変わらないものから分離する

ソフトウェアの仕様には必ず変更が加わるもの。
変わるものと変わらないものを分離しておくことで、
「仕様の変更」に対して「システムの変更」を出来る限り局所的にする。

プログラムはインターフェイスに対して行う(実装に対して行わない)

可能な限り「一般的・抽象的なもの」に対してプログラミングすること。
(ここで言うインターフェイスは、Javaの組み込み構文としてのインターフェイスではなく、
より広いレベルで「抽象度を高めたもの」を意味する。)

これにより、全体のコードの結合度を下げる。

具体性が高く、密結合なコード

if is_car?
  my_car = Car.new
  my_car.drive(200)
else
  my_plane = AirPlane.new
  my_plane.fly(200)
end

乗り物が増える度にコード全体に変更が必要(変更に弱い)。

抽象度が高く、疎結合なコード

my_vehicle = get_vehicle
my_vehicle.travel(200)

乗り物の数が増えても本コードに変更を加える必要はない(変更に強い)。

継承より集約

継承は望ましくないつながりを作ってしまう。

具体的には

  • サブクラスの振る舞いは、スーパークラスの振る舞いに依存する。
  • サブクラスからスーパークラスの中身を覗くことが出来る。
class Vehicle
  def start_engine
    # エンジンを動かすためのもろもろの処理..
  end

  def stop_engine
    # エンジンを止めるためのもろもろの処理..
  end
end

class Car < Vehicle
  def drive
    start_engine
    # driving..
    stop_engine
  end
end
  • Carからエンジンの実装が丸見え
  • エンジンを使用しない乗り物を作りたい場合は大改造が必要

→変わりやすい部分(Engine)を変わりにくい部分(Vehicle)から分離できていない。

代替案

集約を使う。
つまり、

オブジェクトに、「他のオブジェクトに対する参照」を持たせる。

オブジェクトが何かの一種である(is-a-kind-of)関係は避けて、
何かを持っている(has-a)関係にする。

Car has a Engineの例

class Car
  def initialize
    @engine = Engine.new
  end

  def drive
    @engine.start
    # driving..
    @engine.stop
  end
end

class Engine
  def start
    # エンジンを動かすためのもろもろの処理..
  end

  def stop
    # エンジンを止めるためのもろもろの処理..
  end
end

これでEngineがVehicleから分離され、またカプセル化された。

これにより、Engineの手軽な切り替えが可能に。

class Car
  def initialize
    @engine = GasolineEngine.new
  end

  def drive
    @engine.start
    # ガソリンエンジンでドライブ..
    switch_to_diesel
    # ディーゼルエンジンでドライブ..
    @engine.stop
  end

  def switch_to_diesel
    @engine = DieselEngine.new
  end
end

委譲、委譲、委譲

委譲(delegation)

(継承パターンと同じように)start_engineとstop_engineを
クラスの外に公開したいこともある。

そんな場合でも、処理の実態はEngineクラスに任せてしまう。

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

これで「継承より集約」を実現しつつ、継承時と同じ機能を持つクラスが作成できた。
つまり、

集約と委譲の組み合わせは、強力かつ柔軟な継承の代替手段。

※ start_engineやstop_engineは委譲のために書かなければならない無駄なコードに思えるが、
method_missing等の利用で解決できる。
これはProxyパターンで別途詳細する。

必要になるまで作るな(YAGNI/You Ain't Gonna Need It.)

「将来使うかも」は、大抵使わない。

いたずらに柔軟性を持たせようとオーバーエンジニアリングして
コードの複雑性を増していては、本末転倒。

デザインパターン自身は目的ではない。
問題を解決し、目的を達成するための手段として役立てるべきであり、それ以上のものではない。

デザインパターンを考える上での戒め。

以上5原則を前提に

各パターンを見ていく。

【Template Method】-テンプレは準備した、あとはお好きに-
http://qiita.com/kidachi_/items/7c2a80bfc8a87a05051f

【Strategy】-取り替え可能パーツ群を戦略的に利用せよ-
http://qiita.com/kidachi_/items/02f8f6df955ba1d0e93b

【Observer】-本日のニュースをお届けします-
http://qiita.com/kidachi_/items/ce18d2a926c558159689

【Composite】 -世界は再帰的(部分は全体、全体は部分)-
http://qiita.com/kidachi_/items/6cb73b2bbc875e9bef6d

【Iterator】-君の子供たちに伝えたいのだけど-
http://qiita.com/kidachi_/items/afa4c6c29a6eb6be487a

その他WIP.

504
477
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
504
477

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?