読み込み中...

SOLID原則の依存性逆転の原則(DIP)についてRubyで解説

プログラミング #ruby #SOLID #オブジェクト指向 #クリーンコード
2024年6月6日
2024年6月6日
SOLID原則の依存性逆転の原則(DIP)についてRubyで解説

オブジェクト指向プログラミングにおいて、コードの保守性、可読性、再利用性を高めるために、SOLID原則と呼ばれる5つの重要な設計原則があります。

今回は、このSOLID原則のうち、依存性逆転の原則(Dependency Inversion Principle, DIP)についてRubyのサンプルコードを用いて解説していきます。

SOLIDとは

SOLIDとは、5つの原則の頭文字を取った略語です。

  1. Single Responsibility Principle(SRP): 単一責任原則
  2. Open Closed Principle(OCP): オープン・クローズドの原則
  3. Liskov Substitution Principle(LSP): リスコフの置換原則
  4. Interface Separation Principle(ISP): インターフェース分離の原則
  5. Dependency Inversion Principle(DIP): 依存性逆転の原則

依存性逆転の原則 (Dependency Inversion Principle, DIP)とは

依存性逆転の原則は、以下2つの規則で構成されています。

  1. 上位のモジュールは、下位のモジュールに依存してはならない。両者とも抽象に依存すべきである。
  2. 抽象は実装の詳細に依存してはならない。実装の詳細が抽象に依存すべきである。

小難しくて理解しにくいですが、ざっくり言うと、元のクラス(上位モジュール)から他クラス(下位モジュール)を利用したい場合、他クラスを直接コールするのではなく、インターフェースや抽象クラスなどの抽象を介してコールすべきということです。

以降、実際のコードで解説していきます。

依存性逆転の原則に違反しているコード

以下のMySQLDatabaseUserRepositoryクラスを例にします。

# 低レベルのモジュール
class MySQLDatabase
  def initialize
    # ...
  end

  def query(sql)
    # ...
  end
end

# 高レベルのモジュール
class UserRepository
  def initialize
    @db = MySQLDatabase.new
  end

  def find_user(user_id)
    sql = "SELECT * FROM users WHERE id = #{user_id}"
    @db.query(sql)
  end
end

この例では、高レベルのモジュールUserRepositoryが、低レベルのモジュールMySQLDatabaseを直接初期化しており、具体的なクラスに依存しています。

これでは、別のDBを使用したくなった場合やメソッド名変更などの変更がMySQLDatabaseに生じた場合に、UserRepositoryも変更を強いられます。

また、UserRepositoryの単体テストを作成する時、MySQLDatabaseの初期化を強制されるため、MySQLDatabaseの処理によっては、必要なデータの用意や条件分岐のハンドリングなどが必要となり、単体テストがしにくいです。

依存性逆転の原則に則ったコード

以下が則ったコードとなります。

class Database
  def query(sql)
    raise NotImplementedError
  end
end

class MySQLDatabase < Database
  def initialize
    # ...
  end

  def query(sql)
    # ...
  end
end

class UserRepository
  def initialize(database)
    @db = database
  end

  def find_user(user_id)
    sql = "SELECT * FROM users WHERE id = #{user_id}"
    @db.query(sql)
  end
end

UserRepositoryクラスでMySQLDatabaseを直接初期化せず、コンストラクタでDatabaseという抽象クラスを受け取る(依存性注入)ようにしています。(Rubyは言語的にインターフェースがないため、疑似抽象クラスによるダックタイピングでの実装としている。)

これにより、下記の図のようにMySQLDatabaseに対する依存の矢印が逆転していることがわかります。

これが依存性逆転の原則の名前の意味となります。

依存性逆転の原則のメリット

主に以下のメリットがあります。

  1. 変更容易性の向上
    • 下位モジュールを変更する際、抽象を介しているため実装の詳細が隠蔽されており、変更に伴う修正範囲が限定され、バグの発生リスクが低減する。
  2. テスタビリティの向上
    • 抽象に依存しているため、抽象を満たすモックオブジェクトを作成し、コンストラクタ経由でセットすることが可能。
      • これにより、テスト対象でないクラス用にテストデータの用意や条件分岐などの考慮をする必要がなくなり、テストがしやすくなる。
  3. 拡張性の向上
    • 新しい下位モジュールを追加する際、同じ用に抽象を満たすように実装するだけで済み、上位モジュールへの影響を最小限に抑えられる。
  4. 再利用性の向上
    • 抽象を満たす複数の下位モジュールを容易に切り替えて使用できる。

最後に

これにて、SOLID原則の全ての解説が終了となります。

気をつけたいことですが、これはあくまでも原則であり、必ず守らなければいけないという訳ではない点です。

今回ご紹介したDIPも、すべての箇所で適用しようとすると、逆にメンテナンスがしにくくなる場面もあったりします。

大事なのはそれぞれの原則の特徴を知り、今の実装に当てはめるべきかを正しく判断出来るようにすることだと思います。

この部分は経験則が物を言ったりするので、日々この設計は将来どういう問題を起こすだろうか?と自問しながら試行錯誤していくのが良いと思います。



システム開発やDX戦略などでお困りの際はお気軽にご相談ください。

※通常1営業日以内に回答致します。

お問い合わせはこちら
Top