依存性逆転の原則

依存性逆転の原則または依存関係逆転の原則とは、オブジェクト指向設計の用語であり、ソフトウェアモジュールの疎結合を確立する特別な形態を表現したコンセプトである。SOLIDの五原則の一つとして知られる。

オブジェクト指向における従来の依存関係とは、上位モジュールから下位モジュールへの方向性であり、仕様定義を担う上位モジュールを、詳細実装を担う下位モジュールから独立させて、各下位モジュールを別個保存するというものだったが、それに対して依存性逆転原則は以下二点を提唱している。

上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも抽象に依存するべきである。
"High-level modules should not import anything from low-level modules. Both should depend on abstractions ."
抽象は詳細に依存してはならない。詳細が抽象に依存するべきである。
"Abstractions should not depend on details. Details  should depend on abstractions."この上位モジュールと下位モジュールの双方が抽象に依存しなければならないという内容は、それまでの人々のオブジェクト指向の常識を覆しているものだった。

この二点の背景にある考えとは、上位モジュールと下位モジュールの相互作用を設計する際は、その相互作用自体も抽象的に考える必要があるということである。上位モジュールの抽象化だけではなく、それを詳細化する下位モジュールへの見方も変えて、インターフェースの使い方も変えることを求めている。多くの場合、相互作用を抽象的に捉えることは、追加のコーディングパターンを増やすことなくコンポーネント間の結合を減らせることに繋がる。これはより軽量で小規模な実装依存性相互作用スキーマを実現する。

モジュール間で見出された抽象的相互作用スキーマが汎用的な意味をなしているならば、この設計原則は依存性逆転のコーディングパターンを適切な方向に導く。

1. 歴史
-----

依存性逆転の原則は、アメリカのソフトウェア技術者ロバート・C・マーティンによって確立され、彼の論文「オブジェクト指向設計品質指標 - Object Oriented Design Quality Metrics」内の「依存性の分析 - an analysis of dependencies」節を含んだ2000年以降の数々の出版物に記載されていた。「Agile Software Development, Principles, Patterns, and Practices」「Agile Principles, Patterns, and Practices in C#」などのアジャイル系がよく知られる。

依存性逆転の原則と銘打たれたのは、1996年5月のC++研究論文の記事内とされている。

2. 従来のレイヤーパターン
--------------

伝統的なアプリケーションアーキテクチャーにおいて、下位レベルコンポーネントはより複雑なシステムの構築を可能にする上位レベルコンポーネントによって使用される形で設計がおこなわれる。この方法では上位レベルコンポーネントは直接下位レベルコンポーネントに依存する。この低レベルコンポーネントへの依存は、上位レベルコンポーネントの再利用の機会を制限してしまう。

依存性逆転パターンの目指すところは、抽象レイヤーを導入することによってこの高度に結合した状態を回避し、上位の policy layer の再利用性を高めることにある。

3. 依存性逆転パターン
------------

抽象レイヤーを加える事により、上位レベルレイヤーと下位レベルレイヤーの両方とも top から bottom に向かう従来の依存関係を減らす事ができるが、"反転" の概念は下位レベルレイヤーが上位レベルレイヤーに依存することを意味しない。両方のレイヤーは上位レベルレイヤーが要求する振る舞いを表現した抽象に依存すべきである。

依存性逆転の直接のアプリケーションでは、抽象は上位/policy レイヤーによって所有される。このアーキテクチャーでは上位/policyコンポーネントと下位サービスを規定する抽象レイヤーを同一のパッケージとして扱う。低レベルレイヤーはこれらの抽象クラスやインターフェースを継承して生成される。

依存性とオーナーシップの逆転は上位/policyレイヤーの再利用性を高め、上位レイヤーは他の低レベルサービスを利用することができるようになる。もし低レベルレイヤーがクローズドなコンポーネントであったり、アプリケーションが既存のサービスを再利用する必要がある場合、サービスと抽象レイヤーの間を仲介するAdapterを設けるのが一般的である。

4. 依存性逆転パターンの一般化
----------------

多くのプロジェクトにおいて依存性逆転の原則とパターンは一般化されるべき概念であると考えられる。これには少なくとも以下の２つの理由がある。

優れた考えの原則をコーディングパターンとみなすほうがよりシンプルである。ひとたび抽象クラスやインターフェースが実装されるとプログラマーは「自分は抽象化のための仕事をした」と言うかもしれない。
多くのユニットテストツールがモックを作成するためにインターフェースに依存しているため、クラス間のジェネリックなインターフェースを利用することはルールになっている。もし、インターフェースのみに依存するモック作成ツールを使用している場合、一般化された依存性逆転パターンが必要になることがあるが、これには大きな欠点がある。

クラスに対して単純にインターフェースを実装するだけでは不十分であり、一般的に結合を減らすことにはならない。相互作用に対しての潜在的な抽象化を考える事が唯一結合を減らす設計につながり得る。
ジェネリックなインターフェースをプロジェクト内の全ての箇所で実装してしまうと、理解してメンテナンスをするのが非常に難しくなる。ソースコードを読む人は全てのステップにおいて「このインターフェースの他の実装はなんだろうか」と自問することになり、そしてその答えはほとんど「モックだけ」ということになりかねない。
インタフェースの一般化はより "plumbing code" である事が要求され、一般的に依存性注入フレームワークに依存するファクトリーなどが特にそれにあたる。
インターフェースの一般化はプログラミング言語の利用も制限する。

4.1. 一般化における制約
--------------

依存性逆転のパターンを達成するためにインターフェースが存在することは、オブジェクト指向プログラムにおいて他の設計上の制約をもたらす:

クラス内の全てのメンバー変数はインターフェース、もしくは抽象でなくてはならない。
全ての具象クラスパッケージはインターフェース、もしくは抽象クラスパッケージを通してのみ結合されなければならない。
具象クラスを派生してはならない。
既に実装済みのメソッドをオーバーライドしてはならない。
全ての変数のインスタンス化においてFactory MethodもしくはFactory パターンのような生成に関するパターンの実装が必要になる。もしくはDIフレームワークが必要になる。

4.2. インターフェース・モック化の制約
---------------------

継承ベースのモック化ツールを使用した場合でも以下の制約が生じる。

外部公開されている静的メンバーも体系的に依存性注入されるべきだが、そのための実装はかなり難しい。
テスト可能な全てのモジュールはインターフェースの実装、もしくは抽象定義のオーバーライドを行う必要がある。

4.3. 将来的な方向性
------------

原則は考えるための方法であり、パターンは問題解決のための共通手段である。コーディングパターンはプログラミング言語に欠如している機能であるとみなされるかもしれない。

プログラミング言語は少なくとも２つの方向で、その使用においてより正確に、より強力になる方向に進化を続けるだろう。ひとつは使用条件の強化、もう一つは状態ベースのインターフェースである。これらは多くの状況において、より強力な依存性逆転の応用の促進し、潜在的に単純化に寄与するだろう。
　静的メンバーや非仮想メンバーの置き換えの問題を解決するために、今やより多くのモック化ツールがコード注入を用いるようになっている。プログラミング言語は "mocking-conpatible" なバイトコードを生成するように今後進化するかもしれない。 一つの方向性は非仮想メンバーの使用を制限するというもの。そしてもう一つは、少なくともテストを行う状況においては、非継承ベースのモック化が可能なバイトコードを生成するというものだ。

5. 実装
-----

DIPの２つの一般的な実装では、さまざまな意味でよく似た論理アーキテクチャを使用する。

直接的な実装において、 policy クラスと service 抽象クラスは一つのライブラリーにパッケージ化される。この実装では上位コンポーネントと下位コンポーネントは別々のパッケージ/ライブラリとして配布される。上位レベルコンポーネントによって要求される振る舞い/サービスを定義したインターフェースは上位レベルコンポーネントによって所有され、上位レベルコンポーネントと同じライブラリの中に存在する。

上位レベルコンポーネントのインターフェースは下位レベルコンポーネントによって実装されるため、コンパイル時に下位レベルコンポーネントが上位レベルコンポーネントに依存する事が必要になる。よって従来の依存関係は逆転する。

Figures 1 and 2 では同じ機能を実現したコードを表現している。しかし、 Figure 2 ではインターフェースは依存性を逆転させるために使用されている。policy コードの再利用性を最大化したり、循環依存を排除するために依存の方向は選択することができる。

このバージョンの DIP では、下位レイヤーコンポーネントが上位レベルレイヤーのインターフェース/抽象に依存しているため、下位レベルレイヤーの再利用は困難になる。 この実装はその代わりに伝統的な to-to-bottom の依存関係を反対の bottom-to-top へと逆転させる。

抽象コンポーネントをライブラリやパッケージから独立させて置くと、より柔軟性が増す。

全てのレイヤーを分離して個別のパッケージに置くことでどのレイヤーの再利用性も向上し、ロバストネス性とモビリティーを得ることができる。

6. 例
----

6.1. 家系モジュール
------------

ある家系システムでは人々の間の関係を第一レベル関係のグラフとして表現するかもしれない。これは非常に効率的である。

しかし、上位レベルモジュールでは家系をブラウズするためのより簡単な方法が必要になるかもしれない: 人には、子供、父親、母親、兄弟と姉妹(異母兄弟を含む含まないも合わせて）