Skip to main content
カテゴリ: 原則
種類: システム設計原則
起源: ロバート・C・マーティン、C++ Report Engineering Notebook、1996年
別名: DIP、抽象に依存して具象に依存しない、SOLID の D、Dependency Inversion Principle
先に答えると依存性逆転の原則(Dependency Inversion Principle)は、高水準の方針が低水準の詳細に依存してはならず、双方が抽象に依存し、詳細がその抽象に依存すべきだ、という設計規則です。ロバート・C・マーティンは1996年の C++ Report コラムで、プリンタを変えるたびに方針を書き直すコピープログラムを示して命名しました。実務の検査は単純です。ベンダーや装置、担当者を入れ替えるだけで規則そのものを書き直すなら、規則は間違ったものに依存しています。

依存性逆転の原則とは?

依存性逆転の原則は、方針モジュールが、いまたまたま方針を運んでいる具体的な仕組みではなく、自分が所有する抽象に依存すべきだ、という設計規則です。
高水準モジュールは低水準モジュールに依存してはならない。双方が抽象に依存すべきである。抽象は詳細に依存してはならない。詳細が抽象に依存すべきである。
これはインターフェイスを増やせという訴えではありません。問うのは、誰が書き直しを強いてよいかです。方針は、そのシステムが存在する理由です。これをコピーする、あれを払う、これらの学生を入れる。詳細はキーボード、プリンタ、Oracle のドライバ、いまバスを運転している親戚です。方針が詳細を取り込めば、詳細を替えるたびに方針が開き直されます。 日常の絵は壁のコンセントです。やかんは漆喰に半田付けされていません。家がコンセントを公開します。やかん、ランプ、充電器が差し込まれます。やかんを直結すれば、発熱体が壊れたときに壁を剥がすことになります。オープン・クローズド原則 が欲しいのは、家を編集せずに新しいやかんが入ることです。この原則が言うのは、線が走ってよい向きです。家はやかんのブランドに依存しません。やかんが、家がすでに定めたコンセントに依存します。 コードでは、名前で ReadKeyboard()WritePrinter() を呼ぶ Copy() が、同じ半田付けをします。ディスク書き込みが来ると方針に if が生え、次の装置でもう一つ生えます。傷は硬直です。重要なモジュールは、ハードウェアを引きずらなければ再利用もテストもできません。関心の分離 は仕事を分けます。この原則はソース依存の向きを分けます。

依存性逆転の原則を3つの深さで理解する

  • 入門: ランプを家に半田付けしない。コンセントを公開する。別のランプが、配線図を書き直さずに差し込めます。
  • 実務: 方針を、自分が定義したポートに依存させる。ベンダー、ファイル、人はそのポートの後ろへ。ブランド変更で規則を直すなら、矢印はまだ逆です。
  • 上級: 実行時の流れは、いまも方針から詳細へ向かいます。逆転するのはソースコードの矢印です。高水準モジュールが抽象を所有し、詳細が内側へ差し込まれます。ベンダー所有のラッパは逆転ではありません。古い依存に名前が一つ増えただけです。

起源

依存性逆転の原則は、依存性注入フレームワークの標語としてではなく、オブジェクト指向 C++ における結合の診断として始まりました。 ロバート・C・マーティン は、C++ Report の Engineering Notebook 第3回 The Dependency Inversion Principle1996年5月)でこれを述べます。彼はすでに Object Oriented Design Quality Metrics: an analysis of dependencies1994年)で、ソース依存の向きが設計を硬直させるかどうかを決める、と論じていました。1996年のコラムが規則に名前を付けます。伝統的な構造化設計は、高水準モジュールを低水準に依存させ、抽象を詳細に依存させがちだ、と彼は書きます。その癖の修復を、彼は逆転と呼びます。 論文の実務の傷は、装置非依存の I/O がない機械上の Copy プログラムです。方針はループします。キーボードから一字読み、プリンタへ書く。したがって CopyReadKeyboardWritePrinter に依存します。ディスクファイルを足すと、方針にブールと if が芽を出します。マーティンの修正は、Copy の傍らに置く二つの抽象、ReaderWriter です。キーボードとプリンタがそれを実装します。依存は逆転します。詳細が方針のコンセントに依存します。クラスなしでも、C の stdio.h——getcharputchar——が同じ形だと、彼は指摘します。 Agile Software Development: Principles, Patterns, and Practices(Prentice Hall、2003年)は二つの条項を言い直し、ボタンとランプの絵を足します。Lamp を名指すボタンは、後からモータを切り替えられません。ButtonClientSwitchable のコンセントがあれば、ランプが差し込めます。2004年 前後、Michael Feathers はマーティンの「最初の五原則」を SOLID の頭字語に並べます。依存性逆転の原則はその「D」です。 Clean Architecture(Prentice Hall、2017年)では、同じ矢印を依存規則へ広げます。ソース依存は内側へ、方針へ向かう。Martin FowlerInversion of Control Containers and the Dependency Injection Pattern2004年1月23日)が名づけたのは配線の技法——コンストラクタ注入、セッター注入、インターフェイス注入——です。その記事は、オブジェクトが隣人をどう受け取るかです。この原則の改名ではありません。インターフェイス分離の原則 は次の C++ Report コラムで、別の問いを立てます。コンセントを持ったとして、それは広すぎないか。

要点

依存性逆転の原則は、方針がいまの仕組みより長く生きねばならないときに本領を発揮します。具象コードを呼ぶな、ではありません。重要なモジュールが口にしてはならない名前の規則です。
1

逆転するのはソースの矢印であり、実行時の呼び出しではない

実行時、ボタンはいまもランプに点灯を命じます。変わるのはコンパイル時の知識です。方針は詳細を取り込み、派生し、生成してはなりません。詳細は、方針がすでに定義した型を実装します。高水準ファイルがまだ OracleDriver を名指すなら、矢印は逆転していません。段が一つ増えただけです。
2

高水準モジュールがコンセントを所有する

ベンダーのパッケージに住むインターフェイスは、ベンダーの顔であって、あなたの顔ではありません。マーティンの要点は所有です。CopyReaderWriter を定めます。キーボードとプリンタが従う。学校が「土曜のルートを走れること」と書けば、役割は学校のものです。「衛いとこのバンであること」と書けば、コンセントはいとこに渡っています。リスコフの置換 は、次のバンが本当に立てるか、を問います。
3

抽象から詳細を漏らさない

OracleResultSet を返す Database ポートや、一社の無線プロトコルを要求する「バス事業者」契約は、古いプラグが接着されたコンセントです。呼び出し側は詳細に対してコンパイルします。ベンダー型、ファイル形式、個人の癖はポートの後ろへ。単一責任の原則 は、方針に余分な変わる理由を増やさせません。この原則は、それらの理由が取り込まれないようにします。
4

変わらないものは逆転しない

String、安定した言語のリスト、ファイルと一緒に死ぬ一度きりのスクリプトに、ポートは要りません。YAGNI が適用されます。第二の仕組みが本物になったとき、あるいはテストが第一を置き換えるときに、コンセントを発明する。クラスごとにインターフェイスは儀式です。代価は道案内と、設計はもう柔軟だという錯覚です。

応用場面

規則が道具・ベンダー・担当者の交代を生き延びるべきときに、依存性逆転の原則を使います。すでに信頼しているオブジェクトをすべて包むために使わないでください。

規則と I/O のあいだにポートを置く

請求は ClockMailer に依存すべきであり、System.now や去年の SMTP クラスに依存すべきではありません。ポートを方針の隣に書きます。ベンダーはその後ろで適応させます。メール事業者が変わっても、請求規則は開き直しません。

役割を雇い、人を差し込む

週末の当番表が一人の叔父の電話番号を名指せば、半田付けです。職務を書きます。免許、保険、土曜朝7時。名指しの人は一つの実装者です。代理が、学校の方針を書き直さずに差し込めます。これはクラスだけでなく、職務にこの原則を使っています。

ブランド製品ではなく、公共のコンセントを公開する

壁のコンセント、USB、「免許を持つ電気工なら誰でも」と受け付ける市の帳票は、同じ切れ目です。市民は、やかんを替えるために家を建て直すべきではありません。公共サービスが一社のポータルを直書きすれば、調達のたびにサービスを書き直します。コンセントを標準化し、ブランドはその後ろで競わせます。

テストでは本物の仕組みを偽物に替える

生きたデータベースを new する初期キャリアのコードは、電車の中では検査できません。ポートに依存し、既知の行を返す偽物を渡し、方針のテストをネットワークから外します。「Spring をしている」のではありません。重要なモジュールを、いまのハードウェアなしで走らせているのです。

事例

依存性逆転の原則を公開 API として番号付きで見る窓は、**Java Database Connectivity(JDBC)**です。一つのデータベース API がアーキテクチャの全体だ、という主張ではありません。 Sun は 1997年1月 に JDBC を仕様化し、JDK 1.1 とともに 1997年2月19日 に出荷します。API は意図して二層です。アプリケーションコードは java.sql.ConnectionStatementResultSet に話します。ベンダーは java.sql.Driver を実装します。DriverManager.getConnection は、その URL を扱えるドライバを探します。したがって給与モジュールは oracle.jdbc や MySQL クライアントではなく、java.sql に対してコンパイルします。jar を替え、方針は残す。プラットフォームライブラリに落ちたマーティンの逆転した矢印です。高水準コードと低水準ドライバが同じ抽象に依存し、詳細(ドライバ)がその抽象に依存します。 1996年の JDBC 草案は、すでに「アプリケーション作者向け API」と「JDBC ドライバ API」を分けています。Java SE 62006年12月11日)は JDBC 4.0 のサービス読み込みを足します。ドライバが META-INF/services/java.sql.Driver を同梱すれば、Class.forName なしでもプラットフォームが見つけられます。Java SE 262026年)時点でも、java.sql モジュールはこれらのインターフェイスをエクスポートします。OpenJDK の Connection 型は @since 1.1 です。この逆転は、何世代ものデータベースを生き延びています。 限界の注記: JDBC は SQL を移植可能にはしません。Oracle の外部結合構文を埋め込んだ方針は、型がすべて java.sql でも詳細に依存しています。DriverManager 自体は具象のロケータです。後年の講義は DataSource を好みます。新しい設計が写すべきは、1997年の方針とドライバの分割であり、標準 API が方言を消すという主張ではありません。次のデータベースでもクエリを書き直すなら、型は逆転し、言語は半田付けのままです。

限界と失敗パターン

詳細が安定して局所的なとき、依存性逆転の原則は失敗します。StringMath、一ファイルのスクリプトを IString で包んでも、型が増えるだけで第二の実装は買えません。コンセントはコストです。第二の仕組み、テスト用の身代わり、ベンダー交代が本物である場所で払います。 抽象を詳細が所有するときも失敗します。ドライバパッケージの IOracleConnection や、一社の添付レイアウトをまだ要求する「標準」帳票は、方針にベンダーを追わせます。逆転はインターフェイスの存在ではありません。所有の向きです。 よくある誤用は、依存性注入コンテナを原則そのものだと思うことです。コンテナは、具象の OracleRepository を、まだ Oracle 型を取り込んでいる具象の PayrollService に注入できます。配線は逆転しています。依存はしていません。別の誤用は、クラスごとにインターフェイスを置き、それでも一斉に変わる型の迷宮を作ることです。逆転のあとでも デメテルの法則 は効きます。コンセントに話しかけることは、渡されたオブジェクトの奥のベンダーに手を伸ばす免許にはなりません。

よくある誤解

英語名は「依存性注入」、「すべてのクラスにインターフェイス」、「こっちから電話するな、向こうからかける」というハリウッドの一句と衝突します。
注入は届け方です。コンストラクタ引数、セッター、あるいはフレームワークが隣人を渡します。逆転は依存の規則です。その隣人はベンダーのクラスではなく、方針が所有する抽象として型付けされます。具象の Oracle ヘルパーを注入しても、原則には違反できます。mainKeyboardReader を生成しても満たせます。Copy がキーボードを名指していないからです。
マーティンが CopyReaderWriter に対して逆転したのは、それらの仕組みが変わると見込んだからです。すべての整数の前にポートを置け、とは求めていません。安定した言語型、私的な補助、具象の寿命が一つのモジュールは、コンセントを稼ぎません。実装者が永遠に自分自身だけのインターフェイスは、帽子の上の帽子です。
「こっちから電話するな、向こうからかける」が逆転するのは、誰が会話を始めるかです。フレームワークがプラグインを呼びます。この原則が逆転するのは、ソースファイルが口にしてよい名前です。イベントループ、コールバック、コンテナは助けになります。具象の詳細を呼ぶこともできます。検査はコンセントの所有であり、フレームワークが先に鳴ったかどうかではありません。

関連概念

これらのページは同じ問題の隣にあります。いまの道具、ベンダー、担当者が変わるたびに、大切な規則を書き直さずに済ませるにはどうするか。

オープン・クローズド原則

新しい詳細は、方針を編集せずに差し込まれるべきです。この原則は、プラグが方針の所有する抽象を指さねばならない、と言います。

リスコフの置換原則

差し込まれた詳細はコンセントを守らねばなりません。消えないランプは、Switchable を実装していても置換ではありません。

インターフェイス分離の原則

コンセントを持ったら、呼び出し側を決して使わない穴に依存させるな。分離なき逆転は太いプラグです。

単一責任の原則

方針の変わる理由は一つであるべきです。逆転は、余分な理由が具象名として取り込まれるのを止めます。

関心の分離

方針と仕組みを分けておく。この原則は、両者のあいだに残る依存の向きを定めます。

デメテルの法則

目の前のコンセントに話せ。それを通してベンダーの内部オブジェクトへ手を伸ばすな。

一言で言うと

道具、ベンダー、担当者を替えただけで規則を書き直すなら、規則は間違ったものに依存している。コンセントを自分の側に持ち、詳細を差し込めばよい。