オブジェクト指向設計において、依存性逆転の原則は、疎結合ソフトウェアモジュールのための特定の方法論です。この原則に従うと、高レベルのポリシー設定モジュールから低レベルの依存モジュールに確立される従来の依存関係が逆転し、高レベルモジュールが低レベルモジュールの実装の詳細から独立します。この原則は次のように述べられています。[ 1 ]
高レベルオブジェクトと低レベルオブジェクトの両方が同じ抽象化に依存しなければならないと規定することで、この設計原則は、オブジェクト指向プログラミングについて人々が考える方法を逆転させる。 [ 2 ]
この原則のA点とB点の根底にある考え方は、高レベルモジュールと低レベルモジュール間の相互作用を設計する際には、両者間の抽象的な相互作用として捉えるべきだということです。これは、高レベルモジュールと低レベルモジュールの両方の設計に影響を与えます。低レベルモジュールは相互作用を念頭に置いて設計する必要があり、使用インターフェースを変更する必要が生じる場合もあります。
多くの場合、相互作用そのものを抽象的な概念として捉えることで、追加のコーディングパターンを導入することなくコンポーネント間の結合度を低減でき、より軽量で実装依存度の低い相互作用スキーマを実現できます。この抽象的な相互作用スキーマが汎用的かつ明確であれば、この設計原則は後述する依存性逆転パターンにつながります。
従来のアプリケーションアーキテクチャでは、下位レベルのコンポーネント(ユーティリティレイヤーなど)は、上位レベルのコンポーネント(ポリシーレイヤーなど)によって利用されるように設計されており、高度に複合的なシステムを構築できます。ここでは、上位レベルのコンポーネントは、何らかのタスクを実行するために下位レベルのコンポーネントに直接依存します。下位レベルのコンポーネントへのこの依存は、上位レベルのコンポーネントの再利用の機会を制限します。[ 1 ]

依存性反転パターンの目的は、抽象層を介してこの高度に結合された分布を回避し、上位層およびポリシー層の再利用性を高めることです。
抽象レイヤーの導入により、上位レイヤーと下位レイヤーの両方において、従来の上から下への依存関係が軽減されます。しかしながら、「反転」という概念は、下位レイヤーが上位レイヤーに直接依存することを意味するものではありません。むしろ、両方のレイヤーは、上位レイヤーが必要とする動作を公開する抽象化(インターフェース)に依存するべきです。

依存性逆転の直接的な適用では、抽象クラスは上位/ポリシー層によって所有されます。このアーキテクチャでは、上位/ポリシーコンポーネントと下位サービスを定義する抽象クラスが同じパッケージにまとめられています。下位レベルの層は、これらの抽象クラスまたはインターフェースの継承/実装によって作成されます。[ 1 ]
依存関係と所有権の逆転により、上位層とポリシー層の再利用が促進されます。上位層は、下位サービスの別の実装を利用できます。下位層のコンポーネントがクローズされている場合、またはアプリケーションが既存のサービスの再利用を必要とする場合、アダプタがサービスと抽象化の間を仲介するのが一般的です。
多くのプロジェクトでは、依存性逆転の原則とパターンは、ソフトウェアモジュール間のすべてのインターフェースに適用されるべき単一の概念として捉えられています。これには少なくとも2つの理由があります。
使用するモックツールが継承のみに依存している場合、依存性逆転パターンを広く適用する必要が生じる可能性があります。これには大きな欠点があります。
依存性逆転パターン(DIP)を実現するためのインターフェースの存在は、オブジェクト指向プログラムにおいて他にも設計上の意味合いを持つ。
継承ベースのモックツールを使用すると、次のような制約も生じます。
DIPの一般的な実装方法には、類似した論理アーキテクチャを用いるものの、異なる意味合いを持つものが2つ存在する。
直接実装では、ポリシー クラスとサービス抽象クラスを 1 つのライブラリにパッケージ化します。この実装では、高レベル コンポーネントと低レベル コンポーネントは別々のパッケージ/ライブラリに分散され、高レベル コンポーネントが必要とする動作/サービスを定義するインターフェースは、高レベル コンポーネントのライブラリ内に存在し、そのライブラリが所有します。低レベル コンポーネントによる高レベル コンポーネントのインターフェースの実装には、低レベル コンポーネント パッケージがコンパイル時に高レベル コンポーネントに依存する必要があり、従来の依存関係が逆転します。

図1と図2は、同じ機能を持つコードを示していますが、図2ではインターフェースを使用して依存関係を反転させています。依存関係の方向を選択することで、ポリシーコードの再利用を最大化し、循環依存関係を排除することができます。
このバージョンのDIPでは、下位レイヤーのコンポーネントが上位レイヤーのインターフェース/抽象化に依存しているため、下位レイヤーのコンポーネントの再利用が困難です。そこで、この実装では、従来のトップダウン型の依存関係を、ボトムアップ型の依存関係へと「反転」させています。
より柔軟な解決策としては、抽象的な構成要素を独立したパッケージ/ライブラリのセットに抽出する方法がある。

各層をそれぞれ独自のパッケージに分離することで、どの層も再利用しやすくなり、堅牢性と可動性が向上します。[ 1 ]
系図システムでは、人々の間の関係を、それらの間の直接的な関係(父子、父娘、母子、母娘、夫妻、妻夫など)を示すグラフとして表現することができます。これは非常に効率的で拡張性が高く、元夫や法定後見人を簡単に追加できます。
しかし、より上位のモジュールでは、システムを閲覧するためのより簡単な方法が必要になる場合があります。例えば、どの人物にも、子供、両親、兄弟姉妹(異母兄弟姉妹を含む)、祖父母、いとこなどがいます。
系図モジュールの使用方法によっては、共通の関係を個別の直接プロパティとして表現する(グラフを非表示にする)ことで、上位モジュールと系図モジュール間の結合が大幅に軽減され、直接関係の内部表現を、それらを使用するモジュールに影響を与えることなく完全に変更できるようになります。また、兄弟姉妹や叔父の正確な定義を系図モジュールに埋め込むことが可能になり、単一責任の原則を徹底できます。
最後に、最初の拡張可能な一般化グラフアプローチが最も拡張性が高いように見える場合でも、系図モジュールを使用することで、より特化されシンプルな関係の実装でアプリケーションには十分であり、より効率的なシステムの構築に役立つことがわかるかもしれません。
この例では、モジュール間の相互作用を抽象化することで、下位レベルのモジュールのインターフェースが簡素化され、実装もよりシンプルになる可能性があります。
リモートファイルサーバー(FTP、クラウドストレージなど)クライアントは、一連の抽象インターフェースとしてモデル化できます。
ローカルファイルとリモートファイルが同じ抽象インターフェースを提供する場合、依存性逆転パターンを実装する高レベルモジュールは、それらを区別なく使用できます。アプリケーションは、ドキュメントをローカルまたはリモートに透過的に保存できるようになります。
上位モジュールに求められるサービスレベルを考慮する必要がある。
モジュールを抽象的なインターフェースの集合として設計し、他のモジュールをそれに適合させることで、多くのシステムに共通のインターフェースを提供できる。

UI パッケージと ApplicationLayer パッケージには主に具象クラスが含まれています。Controllers パッケージには抽象クラス/インターフェース型が含まれています。UI には ICustomerHandler のインスタンスがあります。すべてのパッケージは物理的に分離されています。ApplicationLayer には、Page クラスが使用する CustomerHandler の具象実装があります。ICustomerHandler インターフェースのインスタンスは、ファクトリ (おそらく同じ Controllers パッケージ内) によって動的に作成されます。具象型 Page と CustomerHandler は、互いに依存するのではなく、ICustomerHandler に依存します。
UI は ApplicationLayer や ICustomerHandler を実装する他の具体的なパッケージを参照していないため、UI クラスを変更することなく CustomerHandler の具体的な実装を置き換えることができます。また、Page クラスは IPageViewer インターフェースを実装しており、これを ICustomerHandler メソッドの引数として渡すことで、CustomerHandler の具体的な実装が具体的な依存関係なしに UI と通信できるようになります。ここでも、両者はインターフェースによってリンクされています。
依存性逆転の原則を適用することは、アダプタパターンの一例として捉えることもできます。つまり、上位クラスは、他の上位クラスが依存する抽象化であるアダプタインターフェースを定義します。適応実装も必然的に同じアダプタインターフェースの抽象化に依存しますが、自身の下位モジュール内のコードを使用して実装できます。上位モジュールは下位モジュールに依存しません。なぜなら、上位モジュールは、適応実装とその下位モジュールによって実装されたインターフェースのポリモーフィックメソッドを呼び出すことによって、アダプタインターフェースを介して間接的に下位機能を使用するだけだからです。
プラグイン、サービスロケーター[ 3 ]、依存性注入[ 4 ] [ 5 ]などのさまざまなパターンが、選択された低レベルコンポーネントの実装を高レベルコンポーネントに実行時にプロビジョニングするために使用されています。
これは、密結合クラスの例です(Javaの場合:
class GasEngine { public void start () { System . out . println ( "ブーン!ガソリンエンジンが始動しました。" ); } }class Car { private GasEngine engine ;public Car () { this.engine = new GasEngine ( ) ; }public void drive () { engine.start ( ) ; } }ここでは、クラスGasEngineと密接に結合しており、依存性逆転の原則に違反しています。これを修正するには、代わりに、依存する抽象化をCar使用できます。EngineCar
interface Engine { void start (); }class GasEngine implements Engine { @Override public void start () { System . out . println ( "ブーン!ガソリンエンジンが始動しました。" ); } }class ElectricEngine implements Engine { @Override public void start () { System . out . println ( "静かな唸り音... 電気エンジンが始動しました。" ); } }class Car { private final Engine engine ;public Car ( Engine engine ) { this . engine = engine ; }public void drive () { engine.start ( ) ; } }依存関係逆転の原則は、Robert C. Martinによって提唱され、論文「Object Oriented Design Quality Metrics: an analysis of dependencies」[ 6 ]、 1996 年 6 月にC++ Reportに掲載された「 The Dependency Inversion Principle 」というタイトルの記事[ 7 ]、書籍「Agile Software Development, Principles, Patterns, and Practices」[ 1 ] 、および「Agile Principles, Patterns, and Practices in C#」など、いくつかの出版物で説明されています。