🦁

Rubyによるデザインパターン~一般的な原則編~

2023/03/05に公開約2,700字

ラスオルセン著の Rubyによるデザインパターンを読んで整理してみました。

デザインパターンによる一般的な原則

  1. 変わるものを変わらないものから分離する。
  2. インターフェースに対してプログラムし、実装に対して行わない。
  3. 継承より集約
  4. 委譲、委譲、委譲
  5. 必要になるまで作るな(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

  • CarEngineに対してなにかしたい場合は、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

ログインするとコメントできます