web-dev-qa-db-ja.com

依存性注入:フレームワークを使用する必要がありますか?

私は最近、Pythonプロジェクトで作業しました。ここでは、依存性注入を多用しました(アプリをテスト可能にするために必要なため)。フレームワークは使用していませんでした。すべての依存関係を手動で接続するのは少し面倒ですが、全体的にはうまくいきました。

オブジェクトを複数の場所に作成する必要がある場合は、プロダクションインスタンスを作成するための関数(そのオブジェクトのクラスメソッドの場合もある)がありました。この関数は、そのオブジェクトが必要なときに呼び出されました。

テストでも同じことを行いました。オブジェクトの「テストバージョン」(つまり、モックオブジェクトに基づく実際のインスタンス)を複数回作成する必要がある場合、この「テストバージョン」を作成する関数がありました。テストで使用されるクラスの。

私が言ったように、これは一般的にうまくいきました。

現在、新しいJavaプロジェクトを入力しています。以前のプロジェクトのように、DIフレームワークを使用するか、DIを手動で実行するかを決定しています。

DIフレームワークでの作業がどのように異なり、手動の「バニラ」依存関係注入よりも良い点と悪い点がどのように異なるかを説明してください。

編集:私も質問に追加したいと思います:フレームワークなしで手動でDIを実行する「プロフェッショナルレベル」のプロジェクトを見ましたか?

24
Aviv Cohn

DIコンテナのキーは抽象化です。 DIコンテナーは、この設計上の懸念を抽象化します。ご想像のとおり、コードに顕著な影響があり、多くの場合、コンストラクタ、セッター、ファクトリ、ビルダーの数が少なくなります。結果として、それらはコードをより疎結合にします(基礎となる IoC により)、よりクリーンでシンプルになります。コストなしではありませんが。

背景

数年前、このような抽象化のために、さまざまな構成ファイル( 宣言型プログラミング )を記述する必要がありました。 LOCの数が大幅に減少するのは素晴らしいことでしたが、LOCSは減少しましたが、構成ファイルの数はわずかに増加しました。中小規模のプロジェクトではまったく問題ではありませんでしたが、大規模なプロジェクトでは、「コードのアセンブリ」がコードとXML記述子の間に50%散在することが問題となりました。それでも、主な問題はパラダイム自体。ディスクリプタはカスタマイズのためのスペースをほとんど残さなかったため、非常に柔軟性がありませんでした。

パラダイムは次第に convention over configuration に移行しました。これは、構成ファイルを注釈または属性と交換します。これにより、コードをよりクリーンかつシンプルに保ちながら、XMLでは不可能だった柔軟性を提供します。

おそらく、これがあなたが今まで働いていた方法に関して最も重要な違いです。より少ないコードとより多くの魔法。

下側に...

構成に関する規約では、抽象化が非常に重くなるため、その仕組みがほとんどわかりません。時には魔法のように見え、ここにもう一つ欠点があります。一度動作すると、多くの開発者はそれがどのように機能するかを気にしません。

これは、DIコンテナでDIを学んだ人にとってのハンディキャップです。彼らは、コンテナによって抽象化されたデザインパターン、優れた実践、原則の関連性を完全には理解していません。開発者にDIとその利点に精通しているかどうかを尋ね、答えた:-はい、Spring IoC-を使用しました。 (一体それはどういう意味ですか?!?)

これらの「欠点」のそれぞれに同意することもしないこともできます。私の控えめな意見では、あなたのプロジェクトで何が起こっているのかを知ることは必須です。そうでなければ、私たちはそれを完全に理解していません。また、DIの目的を理解し、それを実装する方法の概念を理解することは、検討すべきプラスです。

プラス側では...

1つの単語生産性。フレームワークにより、本当に重要なことに集中できます。アプリケーションのビジネス。

コメントされた欠点にもかかわらず、私たちの多く(どの仕事が期限とコストによって条件付けられているか)にとって、これらのツールは非常に貴重なリソースです。私の意見では、これはDIコンテナーの実装を支持する私たちの主な議論であるべきです。 生産性

テスト中

純粋なDIまたはコンテナーを実装する場合でも、テストは問題になりません。まったく逆です。多くの場合、同じコンテナを使用すると、すぐにユニットテストのモックが提供されます。長年にわたり、私はテストを温度計として使用してきました。コンテナ施設に大きく依存していたかどうかを教えてくれます。これらのコンテナーは、セッターやコンストラクターなしでプライベート属性を初期化できるためです。彼らはほとんどどこにでもコンポーネントを注入することができます!

魅力的に聞こえますか?注意してください!罠に落ちないで!

提案

コンテナーを実装することにした場合は、優れたプラクティスに固執することを強くお勧めします。コンストラクタ、セッター、インターフェースの実装を続けます。 フレームワーク/コンテナがそれらを使用するように強制します。別のフレームワークへの移行を簡単にしたり、実際のフレームワークを削除したりできます。コンテナへの依存を大幅に減らすことができます。

この慣行について:

DIコンテナーを使用する場合

私の答えは、主にDIコンテナーを支持する自身の経験によってバイアスされます(私がコメントしたように、私は生産性に重点を置いているため、純粋なDIの必要性はありませんでした。まったく逆です)。

私はあなたが面白いと思うと思います Mark Seemanの記事 この主題について、これは純粋なDIを実装する方法に関する質問にも答えます。

最後に、Javaについて話している場合、まず JSR330-Dependency Injection を確認することを検討します。

まとめ

メリット

  • コスト削減と時間の節約。生産性。
  • 少数のコンポーネント。
  • コードはよりシンプルでクリーンになります。

欠点

  • DIはサードパーティライブラリに委任されます。私達は彼らのトレードオフ、長所と短所に気づくべきです。
  • 学習曲線。
  • 彼らはすぐにいくつかの良い習慣を忘れさせます。
  • プロジェクトの依存関係の増加(より多くのライブラリ)
  • reflectionに基づくコンテナは、より多くのリソース(メモリ)を必要とします。これは、リソースに制約されている場合に重要です。

純粋なDIとの違い

  • より少ないコードとより多くの構成ファイルまたは注釈。
19
Laiv

私の経験では、依存性注入フレームワークはいくつかの問題を引き起こします。一部の問題は、フレームワークとツールに依存します。問題を軽減する方法があるかもしれません。しかし、これらは私が直面した問題です。

非グローバルオブジェクト

場合によっては、グローバルオブジェクト(実行中のアプリケーションでインスタンスを1つだけ持つオブジェクト)を注入する必要があります。これは、MarkdownToHtmlRenderererDatabaseConnectionPool、apiサーバーのアドレスなどです。これらの場合、依存関係注入フレームワークは非常に簡単に機能します。必要な依存関係をコンストラクターに追加するだけで、それを持っています。

しかし、グローバルではないオブジェクトはどうでしょうか?現在のリクエストにユーザーが必要な場合はどうなりますか?現在アクティブなデータベーストランザクションが必要な場合はどうなりますか?イベントを発生させたばかりのテキストボックスであるフォームを含むdivに対応するHTML要素が必要な場合はどうでしょうか。

DIフレームワークで採用されているソリューションは、階層型インジェクターです。ただし、これにより、注入モデルがより複雑になります。階層のどの層から何が注入されるかを理解するのが難しくなります。特定のオブジェクトがアプリケーションごと、ユーザーごと、リクエストごとなどに存在するかどうかを判断するのは困難になります。依存関係を追加して機能させるだけではもはや不可能です。

図書館

多くの場合、再利用可能なコードのライブラリが必要になります。これには、そのコードからオブジェクトを作成する方法も含まれます。依存性注入フレームワークを使用する場合、これには通常、バインディングルールが含まれます。ライブラリを使用する場合は、ライブラリにバインディングルールを追加します。

ただし、さまざまな問題が発生する可能性があります。同じクラスが異なる実装にバインドされる可能性があります。ライブラリは、特定のバインディングが存在することを期待する場合があります。ライブラリが一部のデフォルトのバインディングをオーバーライドして、コードが奇妙な動作をする可能性があります。コードがデフォルトのバインディングをオーバーライドして、ライブラリコードが奇妙な動作をする場合があります。

隠された複雑さ

手動でファクトリーを作成すると、アプリケーションの依存関係がどのように組み合わされるかがわかります。依存性注入フレームワークを使用する場合、これは表示されません。代わりに、遅かれ早かれすべてがほとんどすべてに依存するまで、オブジェクトはますます依存関係を拡大する傾向があります。

IDEサポート

あなたのIDEはおそらく依存性注入フレームワークを理解しないでしょう。手動のファクトリーでは、コンストラクターのすべての呼び出し元を見つけて、オブジェクトが構築される可能性のあるすべての場所を見つけることができます。しかし、それはうまくいきません。 t DIフレームワークによって生成された呼び出しを見つけます。

結論

依存性注入フレームワークから何が得られますか?オブジェクトをインスタンス化するために必要な単純なボイラープレートを避けることができます。私は単純な定型文を排除するためにすべてです。ただし、単純な定型文を維持するのは簡単です。そのボイラープレートを交換する場合は、もっと簡単なものに交換するように強く主張する必要があります。上で説明したさまざまな問題があるため、フレームワーク(少なくとも現状では)が適切であるとは思いません。

10
Winston Ewert

Java +依存性注入=フレームワークが必要ですか?

番号。

DIフレームワークは何を購入するのですか?

Java以外の言語で発生するオブジェクト構築。


ファクトリーメソッドがニーズに十分対応できる場合は、Java 100%本物のpojo Javaで完全にフレームワークフリーでDIを実行できます。ニーズがそれ以上の場合は、他のオプションがあります。

Gang of Fourのデザインパターンは多くの素晴らしいことを実現しますが、創造的なパターンに関しては常に弱点がありました。 Java自体にもいくつかの弱点があります。DIフレームワークは、実際のインジェクションよりもこの作成上の弱点を実際に助けます。それらを使用せずにそれを行う方法をすでに知っています。どれほど優れているでしょうか。オブジェクトの構築にどれだけの助けが必要かによって異なります。

ギャングオブフォーの後に出てきた多くの創造的なパターンがあります。 Josh Blochビルダーは、Javaの名前付きパラメーターの欠如を回避する素晴らしいハックです。それ以上に、柔軟性の高いiDSLビルダーがあります。しかし、これらはそれぞれ重要な作業と定型を表しています。

フレームワークは、この退屈な作業からあなたを救うことができます。彼らには欠点がないわけではありません。注意しないと、フレームワークに依存していることに気付くでしょう。フレームワークを使用した後、フレームワークを切り替えてみてください。どういうわけかxmlまたは注釈を一般的なままにしても、プログラミングジョブをアドバタイズするときに、並べて言及しなければならない基本的に2つの言語をサポートすることになります。

DIフレームワークの能力に夢中になる前に、少し時間がかかり、DIフレームワークが必要だと思われるかもしれない機能を提供する他のフレームワークを調査してください。多くは simple DI を超えて分岐しています。考慮してください: LombokAspectJ 、および slf4J 。それぞれがいくつかのDIフレームワークと重複していますが、それぞれが単独で正常に動作します。

テストがこれの原動力である場合は、以下を調べてください。 jUnit(any xUnit really) および Mockito(本当にモック) DIフレームワークがあなたの人生を引き継ぐべきだと考える前に。

あなたは好きなことをすることができますが、真剣な仕事のために私はスイスのアーミーナイフよりも人が多いツールボックスを好みます。これらの線を描く方が簡単だったらいいのにと思っていますが、一部はぼかすために一生懸命働いてきました。何も問題はありませんが、これらの選択が困難になる可能性があります。

3
candied_orange

クラスを作成して依存関係を割り当てることは、モデリングプロセスの一部である楽しい部分です。それが、ほとんどのプログラマーがプログラミングを楽しむ理由です。

どの実装を使用するか、どのようにクラスを構築するかを決定することは、なんらかの方法で実装する必要があり、アプリケーション構成の一部です。

手動で工場を書くことは退屈な仕事です。 DIフレームワークは、解決される可能性のある依存関係を自動的に解決し、解決されない可能性のあるものに対して構成を必要とすることで、より簡単になります。

最新のIDEを使用すると、メソッドとその使用法をナビゲートできます。手動で作成したファクトリーは非常に優れています。クラスのコンストラクターを強調表示し、その使用法を示すだけで、特定のクラスが構築されているすべての場所と方法が表示されるからです。

しかし、DIフレームワークを使用しても、オブジェクトグラフ構築構成(今後はOGCC)などの中心的な場所を確保でき、クラスが曖昧な依存関係に依存している場合は、OGCCを調べて特定のクラスがどのように構築されているかを確認できますすぐそこに。

特定のコンストラクターの使用法を見ることができるのは素晴らしいプラスだと個人的に考えることに反対することもできます。あなたにとってより良いものは主観的であるだけでなく、主に個人的な経験から来ると私は言います。

DIフレームワークに依存するプロジェクトに常に取り組んできたなら、本当にそれだけを知っており、クラスの構成を確認したい場合は、常にOGCCを調べ、コンストラクターの使用方法を確認することすらしません。とにかく、とにかく依存関係がすべて自動的に結び付けられていることがわかるでしょう。

当然のことながら、DIフレームワークをすべての場合に使用できるわけではなく、ファクトリメソッドを1つまたは2つ作成する必要があります。非常に一般的なケースは、コレクションの反復中に依存関係を構築することです。各反復は、別の方法では一般的なインターフェースの異なる実装を生成する異なるフラグを提供します。

結局のところ、それはおそらくあなたの好みが何であるか、そしてあなたが犠牲にして喜んで何をするかということになるでしょう(時間を書く工場か透明性のどちらか)。


個人的な経験(警告:主観的)

PHP=開発者として話していますが、DIフレームワークの背後にあるアイデアは好きですが、私は一度しか使用していません。そのとき、私が作業しているチームがアプリケーションを非常に迅速にデプロイするはずでした。工場を書く時間を無駄にすることはできなかったので、単純にDIフレームワークを取得して構成し、それが機能しましたなんとか

ほとんどのプロジェクトでは、DIフレームワーク内で起こる黒魔術が嫌いなので、ファクトリーを手動で作成するのが好きです。クラスとその依存関係のモデル化を担当したのは私です。なぜデザインがデザインに似ているのかを判断したのは私でした。したがって、クラスが適切に構築されるのは私です(クラスがインターフェースではなく具象クラスなどのハード依存関係を持っている場合でも)。

2
Andy

個人的に私はDIコンテナーを使用せず、それらを好きではありません。その理由は他にもいくつかあります。

通常、これは静的な依存関係です。したがって、これは サービスロケータ であり、欠点がすべてあります。次に、静的依存関係の性質により、それらは非表示になります。あなたはそれらに対処する必要はありません、彼らはどういうわけかすでにそこにあり、あなたがそれらを制御するのをやめるのでそれは危険です。

それは breaks OOP principals のように、凝集力や David West のレゴブリックオブジェクトのメタファーなどです。

したがって、オブジェクト全体を可能な限り プログラムのエントリポイント の近くに構築する方法です。

2
user286277

すでにお気づきのように:使用する言語に応じて、さまざまな見方があり、diffenrentは同様の問題への対処方法を引き受けます。

依存性注入に関しては、これに帰着します:依存性注入は、用語が言うように、1つのオブジェクトに必要な依存関係を置くことintothat object-対照的に:オブジェクト自体に、依存するオブジェクトのインスタンスをインスタンス化させます。 オブジェクトの作成オブジェクトの消費を分離して、柔軟性を高めます。

デザインがうまくレイアウトされている場合、通常は多くの依存関係を持つクラスがほとんどないため、手動またはフレームワークによる依存関係の配線は比較的簡単で、テストを書くためのボイラープレートの量は簡単に管理できるはずです。

とはいえ、DIフレームワークは必要ありません。

しかし、なぜそれでもフレームワークを使用したいのですか?

I)ボイラープレートコードを避けます

プロジェクトがサイズを取得し、ファクトリ(およびインスタレーション)を何度も作成する苦痛を感じる場合は、DIフレームワークを使用する必要があります

II)プログラミングへの宣言的アプローチ

JavaがかつてXMLでのプログラミングだった場合):springing fairydust withアノテーションマジックが起こります

しかし真剣に:私はこれに明確なpsideを見ます。あなたはそれを見つける場所とそれを構築する方法の代わりに何が必要かを言うことで意図を明確に伝えます。


tl; dr

DIフレームワークはコードベースを魔法のように改善するわけではありませんが、見栄えのよいコードを実際に見栄えよくするのに役立ちますcrisp。必要だと感じたときに使用してください。プロジェクトの特定の時点で、時間を節約し、吐き気を防ぐことができます。

1
Thomas Junk

はい。おそらく、Mark Deemannの "Dependency Injection"という本を読んでください。彼はいくつかの良い習慣を概説します。あなたが言ったことのいくつかに基づいて、あなたは恩恵を受けるかもしれません。作業単位(Webリクエストなど)ごとに1つのコンポジションルートまたは1つのコンポジションルートを目指す必要があります。作成したオブジェクトは依存関係(メソッド/コンストラクターのパラメーターと同様)と見なされるという彼の考え方が好きなので、オブジェクトを作成する場所と方法には十分注意する必要があります。フレームワークは、これらの概念を明確に保つのに役立ちます。マークの本を読んだら、あなたはあなたの質問に対する答え以上のものを見つけるでしょう。

0
keith

はい、Javaで作業する場合、DIフレームワークをお勧めします。これにはいくつかの理由があります。

  • Javaにはいくつかの非常に優れたDIパッケージがあり、自動化された依存関係の挿入を提供します。通常、単純なDIよりも手動で書くのが難しい追加機能がパッケージされています(たとえば、コードを生成してスコープを自動的に接続し、どのスコープにあるべきかを調べることができるスコープ付きの依存関係があります。 dを使用して、そのスコープの依存関係の正しいバージョンを見つけます。たとえば、アプリケーションスコープのオブジェクトは、リクエストスコープのオブジェクトを参照できます。

  • Javaアノテーションは非常に有用であり、依存関係を構成する優れた方法を提供します

  • Javaオブジェクトの作成は、Pythonで慣れ親しんでいるものに比べて非常に冗長です。ここでフレームワークを使用すると、動的言語よりも多くの作業を節約できます。

  • Java DIフレームワークは、動的言語よりもJavaで動作するプロキシやデコレータを自動的に生成するなどの便利な機能を実行できます。

0
Jules

プロジェクトが十分に小さく、DIフレームワークの経験があまりない場合は、フレームワークを使用しないにレコメンドし、手動でdiを実行します。

理由:以前は、異なるdiフレームワークに直接依存する2つのライブラリを使用するプロジェクトで働いていました。彼らは両方ともdi-give-me-an-instance-of-XYZのようなフレームワーク固有のコードを持っていました。私が知る限り、アクティブなdi-framework実装は一度に1つしか持てません。

このようなコードでは、利益よりも害を及ぼす可能性があります。

より多くの経験がある場合は、おそらくインターフェイス(ファクトリーまたはサービスロケーター)によってdi-frameworkを抽象化するので、この問題はもう存在せず、diフレームワークはその害により多くの利益をもたらします。

0
k3b