私は、ベストプラクティス、アーキテクチャパターン、およびデザインプリンシパルの学生です。私は最近、依存性注入と制御の逆転について多くの研究を行っており、過去半年かそれ以上の私のプロジェクトの多くでかなり広範囲にわたって「クーラードを飲んで」います。
最近、上司とこれについて話し合いました。特に私たちの特定の環境に当てはまる依存性注入の利点を説明するように彼に頼みました。彼自身は開発者ではありませんが、プログラミング言語と関連する概念を理解する鋭い能力があります。具体的には、依存関係注入を使用する方がAPIとして機能するコアライブラリを使用するよりも優れている理由を彼に説明したかったのです。言い換えると、これは、インフラストラクチャモジュール(データアクセス)をコアに持つアプリケーションを伝統的に構築する方法です。
これは、DIP/DIを使用した場合の外観です。
もちろん、各図の矢印は依存関係の方向を表しています。
アプリケーションの他の領域に影響を与えずにコンポーネントを交換でき、新しいコンポーネントをターゲットにするために多くのコードを書き直す必要があるため、疎結合コードは常に優れていると私は彼に言いました。具体的なAPIではなく、抽象化へのコーディングが望ましいと説明しました。抽象化の観点から考える必要があり、具体的な実装に固有の要素が他のモジュールにリークする可能性が低くなるためです。また、コンポーネントを分離してテストし、外部システムのモックを使用する方が簡単であることも説明しました。
とりあえずテストの引数をとっておくと、彼はパブリックインターフェースに影響を与えずにインフラストラクチャモジュールを変更できない理由を知りたがっていました。たとえば、永続化テクノロジとしてSQL ServerからOracleに変更することを決定した場合、それらの変更をデータアクセスモジュールで行うだけで、その上のモジュールは賢くなりません。
彼には良い答えがありませんでした(テストの利点以外)。特定の環境でSQL ServerからOracleに切り替えることはほとんどないので、「ベストプラクティスに過ぎない」と言って、前に述べた理由を繰り返します。
私の主張を強化するために他に何を言ったでしょうか?
依存関係の逆転の原則の主な目的は、依存関係の方向を変えることです。
この例では、UIレイヤーはプレゼンテーションレイヤーに依存し、プレゼンテーションレイヤーはビジネスロジックレイヤーに依存しています。したがって、ビジネスロジックに変更を加える場合は、下位レベルのレイヤーに依存しているため、すべてのレイヤーを再コンパイルする必要があります。
実装の内部的な側面のみを変更し、パブリックAPIをそのままにするため、実装に依存するモジュールに影響を与えないので、実装をその場で変更できないのはなぜですか
詳細/実装の変更を上位レベルのレイヤーから分離するため
下位レベルのレイヤーに変更を加える場合、このレイヤーのみを再コンパイルする必要があります。
これが、上位層が下位層に期待するインターフェースを提供する必要がある理由です。下位層はこのインターフェースを実装します。
したがって、アプリケーションでは、すべての実装に依存する、アプリケーションのエントリポイントである1つのレイヤーのみが作成されます。これらを組み合わせるためのこのレイヤーの役割。
ここで、依存性注入が役立ちます。ほとんどの依存関係注入フレームワークは、すべての依存関係(実装)を動的にロードする可能性を提供します。再インストールせずにソフトウェアを更新する可能性があるため、更新された実装ライブラリのみをユーザーに提供します。
依存矢印の方向
Run-time dependencies Compile-time dependencies
------------------------------- -------------------------------
| | | |
| UI Layer | | UI Layer |
| | | |
----INeededBehvaiorInterface--- ----INeededBehvaiorInterface---
| ^
v |
------------------------------ ------------------------------
| | | |
| Business logic | | Business logic |
| | | |
-----IDataServiceInterface----- -----IDataServiceInterface-----
| ^
v |
------------------------------ ------------------------------
| | | |
| Data layer | | Data layer |
| | | |
------------------------------- -------------------------------
ここで提示するものは一般化されているため、例と質問の設定方法によって答えが非常に難しくなります。したがって、上司への応答は一般的な議論です(ところで、これは完全に正しいです)。具体例を見てそのメリットを示しれば、強い主張ができると思います。
一般的な注意事項として、いくつかの注意事項/アイデアがあります。
2番目のグラフがDIの概念を正しく示しているとは思いません。ファビオの答えは、境界をよりよく示し、DIがどのように適合するかを示しています
ロバートは、「具体的なAPIではなく抽象化へのコーディングが望ましい」と書くときに、どの抽象化を参照するかを明確にするように依頼されたとき、別の重要なポイントを挙げようとしました。 APIは定義による抽象化の表現であるため、正しい意味は実装に対してではなく、抽象化に対してコーディングすることだと思います
(2)に基づいて、DIアプローチで得られるのは、その結果としてインターフェースの使用で得られる拡張性です(SOLID設計の "Open/Close"プリンシパル)。ここにあります。例:
たとえば、YouBusinessLayerがIDataRepositoryを受け入れ、後者がSQL Serverへのアクセスを提供するとします。
正しく言うと、Oracleに変更した場合、新しいIDataRepository(OracleDataRepo)の子孫を開発して、YourBusinessLayer(SQLDataRepo)ではなくYourBusinessLayer(OracleDataRepo)にプラグインできます。
だから、今あなたはこの階層を持っています:
IDataRepository-> OracleDataRepoおよびIDataRepository-> SQLDataRepo
この段階では、実装を変更できるので、上司にポイントがあります。
しかし、既存のデータ層に追加機能を提供したい場合はどうでしょう。ケースの詳細がわからないので、関連するケースを選択します。サーバーにデータをアップロードしたサードパーティプロバイダーのログサービスを使用するとします。
現在の設計を使用する場合は、継承とポリモーフィズムを使用する必要があり、OracleクラスとSQLクラスがある場合は、両方を更新する必要があります。 DIとインターフェイスを使用して、IDataRepositoryを確認し、コードの1箇所でのみ変更します。
次に、データアクセス層の一部の開発を担当する別のチームがある場合はどうですか(ここでも、ツアーの実際の詳細はわかりません)。たとえば、ロガーは他の誰かによって開発されました。彼らはあなたのクラスでどのように働きますか?作業を停止して、コードへのアクセスを許可する必要があります。
インターフェースとDIを使用すると、継承ではなく構成を優先することで、うまく機能します。つまり、依存関係注入を使用して、インターフェースを介してロガーを受信します。
そして私が考えることができる最後の1つの例はこれです:クライアント(またはデータアクセスレイヤーのユーザー)が機能に関して異なるニーズを持っていて、すべての機能を全員に公開したくない場合はどうでしょうか。 DIとインターフェイスを使用して、IDataRepositoryを継承し、それに応じてルールを適用すると、古いステートメントYourBusinessLayer(SQLDataRepo)が新しいインターフェイスを適切に使用します。