2
1

More than 1 year has passed since last update.

変更に強いコードを設計する(DIパターン)

Last updated at Posted at 2022-10-18

初めに

適切に設計されたオブジェクトは単一責任になっているので実際のアプリケーションでは複数のオブジェクトを組み合わせて仕事をしています。あるクラスから他のクラスを呼び出したり、モジュールをインクルードしたり。そうすることで相互に依存が発生します。依存が発生すること自体は避けられませんが、依存の方法を管理することで変更に強いコードにすることが可能です。

管理しやすいコード、変更に強いコードを書けるようになりたくてオブジェクト指向について学習しています。
学んだことをまとめます。

依存関係があるとはどんな状態か

オブジェクトが以下について知っているときは依存関係がある状態です。

  1. 他のクラスの名前
  2. self以外に送ろうとしているメッセージ(メソッド)の名前
  3. メッセージが要求する引数
  4. そのメッセージが要求する引数の順番

コード例

class Gear
  def initialize
  end

  def gear_inches
    ratio * Wheel.new(rim, tire).diameter
  end
end

Gearクラスは

  • Wheelというgear_inchesメソッド内でクラス名を知っている(1)
  • Wheelクラスにdiameterメソッドがあることを知っている(2)
  • Wheelクラスの初期化に rim, tire が必要ということを知っている(3)
  • rim, tireという順でnewメソッドに渡さないといけないということを知っている(4)

正しく設計されたオブジェクトは単一責任なので依存関係は発生します。
ただ、依存の仕方を工夫することで変更に強いコードにすることができます。

依存を注入する(DIパターン)

Wheelというクラス名を知っている(1) に対応する方法としてDIパターン(dependency injection)があります。外部クラスをメソッド内で初期化して使用するのではなく、外部クラスのインスタンスを受け取ってそれを使用するという方法です。
コード例

class Gear
  attr_reader :wheel
  def initialize(wheel)
    @wheel = wheel
  end

  def gear_inches
    ratio * wheel.diameter
  end
end

gear_inchesでWheelクラスをnewするのではなくGearクラスを初期化する際に引数として受け取るようにしました。
この変更で受けられるメリットは

  • Wheelクラスのクラス名が変更されたとしても影響が及ばなくなった
  • diameterというメソッドを持つクラスなら受け入れられるようになった

今まではWheelクラスのdiameterと固定されていたのが、さまざまなクラスで再利用できるようになりました。
インスタンスを受け取るようにしたので
Wheelクラスの初期化に rim, tire が必要ということを知っている(3)
rim, tireという順でnewメソッドに渡さないといけないということを知っている(4)
についてもGearクラスは知る必要がなくなって疎結合になりました。(4)の順番についてはRubyであれば引数をハッシュで渡すかキーワード引数を使うことで順番に関する知識を持つ必要がなくなります。

このような対応が制約上取れないといった場合はWheelクラスをgear_inchesで初期化するのではなく初期化用のメソッドに切り出すなどの方法をとることでGearクラスがWheelクラスに依存しているということを表現することができます。

参考

2
1
1

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
2
1