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


24

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

オブジェクトを複数の場所で作成する必要があった場合、実稼働インスタンスを作成する関数(場合によってはそのオブジェクトのクラスメソッド)が必要でした。この関数は、そのオブジェクトが必要になるたびに呼び出されました。

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

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

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

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

編集:私も質問に追加したいと思います:フレームワークなしで手動でDIを行う「プロレベル」プロジェクトを目撃しましたか?


私の答えの横に。どちらの方法にも互換性があります。フレームワークへのソフトな依存性注入を残し、重要なコンポーネントのファクトリを保持できます。モデルまたはビジネスに関連するコンポーネント。
ライヴ

4
あなたはすでにチェックしていると思います、あなたがそうしない場合だけで、なぜ私たちはDIのためのフレームワークが必要なのですか?
ライヴ

回答:


19

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

背景

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

パラダイムは、設定ファイルをアノテーションや属性と交換する設定よりも慣習に徐々に移行しました。コードをよりクリーンでシンプルに保ちながら、XMLにはない柔軟性を提供します。

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

欠点は...

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

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

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

プラス面では...

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

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

テスト中

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

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

提案

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

この慣行に関して:

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

私の答えは、主にDIコンテナを好む自身の経験によって偏っています(私がコメントしたように、私は生産性に重点を置いているので、純粋なDIの必要はありませんでした。まったく反対です)。

このテーマに関するMark Seemanの興味深い記事は、純粋なDIの実装方法に関する質問にも答えていると思います。

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

要約する

メリット

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

欠点

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

純粋なDIとの違い

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

1
「これらのフレームワークは、ユニットテストや整合性テストを行うためにプライベートではないので、テストについて心配する必要はありません。」この言葉遣いはタイプミスだと思わせますが、デコードに問題があります。
candied_orange

これは、デバッグに苦労したり、テストのためにフレームワークを使用したりしても、これらのフレームワークを使用するために破棄するのに十分な欠点ではないことを意味します。あなたがそれらを嫌っていても、彼らはまだ試してみる価値がある利点をまだ持っているからです。大きな欠点は、それ自体がフレームワークではありません。それらの使い方です。フレームワークが必要としない場合でも、インターフェースやセッターなどのいくつかの良い習慣が必要です。最後に、フレームワークのこれらの並べ替え(DI)でイースリー統合テストのために良いLIBSがある
LAIV

それがあなたが意味するものである場合:「これらのフレームワークはユニットテストや整合性テストの実行を妨げないので、テストに与える影響を心配しないでください」
candied_orange

急いで これらのフレームワークでもコードをテストできると言っています。抽象化にもかかわらず、テストは依然として実行可能です。回答を自由に編集してください。ご覧の通り、私の英語はまだまともではありません:-)
Laiv

1
Dagger2のDIはわずかに異なることに注意してください。グルーコードは、コンパイル時に通常の読み取り可能なJavaソースとして生成されるため、ランタイムマジックを処理する代わりに、物事がどのように接着されるかを追跡する方がはるかに簡単です。
トールビョーン・ラヴン・アンデルセン

10

私の経験では、依存性注入フレームワークは多くの問題につながります。問題の一部はフレームワークとツールに依存します。問題を軽減するプラクティスがあるかもしれません。しかし、これらは私が遭遇した問題です。

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

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

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

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

図書館

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

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

隠れた複雑さ

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

IDEサポート

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

結論

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


これは非常に良い答えですが、クラスライブラリにIoCコンテナを使用することは想像できません。それは私には貧弱なデザインのように感じます。ライブラリには、必要な依存関係があればそれを明示的に提供してくれるはずです。
ラバーダック

私の場合、@ RubberDuckは、同じ依存性注入フレームワークですべて標準化された多数のJavaプロジェクトを持つ大企業で働いていました。その時点で、一部のチームは、賢明な外部インターフェイスを提供するのではなく、DIフレームワークによってまとめられることを期待しているライブラリをエクスポートするのが魅力的になります。
ウィンストンイーバート

それは残念です@WinstonEwert。コンテキストを記入してくれてありがとう。
ラバーダック

手動注入の設計時型バインディングとは対照的に、注入フレームワークのランタイムバインディングを使用して、IDEパーツに最適です。コンパイラは、手動の場合、忘れられた注入を防ぎます。
バシレフス

IntelliJのは理解CDI(Java EEの標準DI)
するThorbjörnRavnアンデルセン

3

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

いや

DIフレームワークは何を購入しますか?

Java以外の言語で行われているオブジェクトの構築。


ファクトリメソッドがニーズに十分であれば、100%本格的なpojo javaで完全にフレームワークなしでJavaでDIを実行できます。ニーズがそれを超える場合、他のオプションがあります。

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

Gang of Fourの後に出てきた多くの創造的なパターンがあります。Josh Blochビルダーは、Javaの名前付きパラメーターの欠如に関する素晴らしいハックです。さらに、多くの柔軟性を提供するiDSLビルダーがあります。しかし、これらはそれぞれ重要な作業と定型文です。

フレームワークは、この退屈からあなたを救うことができます。欠点がないわけではありません。注意を怠ると、フレームワークに依存することになります。フレームワークを使用した後にフレームワークを切り替えてみて、自分で確かめてください。何らかの方法でxmlまたはアノテーションを汎用的に保持していても、プログラミングジョブを宣伝する際に並べて言及しなければならない基本的に2つの言語をサポートすることになります。

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

テストは本当ににこのしてください見ての原動力である場合:JUnitの (本当にすべてのxUnit)Mockito (本当にすべてのモック)あなたはDIフレームワークを考え始める前に、あなたの人生を引き継ぐ必要があります。

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


PowerMockはこれらのケースのいくつかを解決しました。少なくとも静力学のモックアップを可能にします
Laiv

えー DIを適切に実行している場合、IoCコンテナフレームワークを別のものに交換するのは比較的簡単です。これらの注釈をコードから外すと問題ありません。基本的に、コンテナをサービスロケータとして使用する代わりに、IoCコンテナを使用してDIを実行します。
ラバーダック

@RubberDuck正しい。複数のDIフレームワークの下でコードベースを常にテストおよび保守することを除いて、「DIを適切に実行している」ことを確認する簡単で信頼できる方法を知っていますか?Javaでpojo以外を使用しなくても、XMLの作成に多大な努力が払われたとしても、XMLがIoCコンテナーに依存している場合、それは単純なスワップではありません。
candied_orange

@CandiedOrange相対的な用語として「シンプル」を使用しています。物事の大規模なスキームでは、ある構成ルートを別の構成ルートに置き換えることは、それほど難しくありません。数日はトップになります。DIを適切に実行していることを確認するために、それがコードレビューの目的です。
ラバーダック

2
@RubberDuck私が見たほとんどの「構成ルート」は、メインで約5行のコードです。だから 過度に難しい作業ではありません。私が警告しているのは、これらの行をエスケープする独自のものです。あなたはそれを「正しくDIする」と呼びます。コードレビューで問題を証明するために何を使用するかを尋ねています。または、「Meh」とだけ言いますか?
candied_orange

2

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

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

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

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

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

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

DIフレームワークに依存しているプロジェクトに常に取り組んでいた場合、それだけを知っているはずです。クラスの構成を確認したいときは、常にOGCCを調べて、コンストラクターがどのように使用されているかを見ようともしませんでしたとにかく、依存関係がすべて自動的に接続されることがわかっているからです。

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

最終的には、すべてがあなたの好みが何であり、何を犠牲にしたいのか(工場を書くか、透明性のどちらか)になるでしょう。


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

PHP開発者として言えば、DIフレームワークの背後にあるアイデアは好きですが、一度しか使用していません。そのとき、私が働いていたチームが非常に迅速にアプリケーションをデプロイすることになっていたので、工場を書く時間を失うことはありませんでした。だから、私たちは単にDIフレームワークを選択し、それを設定し、何とか動作しました

ほとんどのプロジェクトでは、DIフレームワーク内で発生するブラックマジックが気に入らないため、工場を手動で作成するのが好きです。クラスとその依存関係のモデリングを担当したのは私です。私は、デザインがそのように見える理由を決定した人でした。したがって、クラスを適切に構築するのは私です(クラスがインターフェイスではなく具体的​​なクラスなどのハードな依存関係をとる場合でも)。


2

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

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

これ、結合性やデビッドウェストのレゴブリックオブジェクトのメタファーなど、OOPの原則を破ります。

そのため、プログラム全体のエントリポイントをプログラムのエントリポイントのできるだけ近くに構築するのが方法です。


1

すでにお気づきのように、使用する言語に応じて、さまざまな観点がありますが、同様の問題に対処する方法は異なります。

依存性注入にに関しては、これに尽きる:用語が言うように依存性の注入は、何より、ある依存関係を一つのオブジェクトのニーズを置くこと 、そのオブジェクト -他の方法を対照的な:オブジェクトのオブジェクト自体instatiateインスタンスをさせることが異なります。オブジェクト作成を分離しますオブジェクト消費をして、柔軟性を高めます。

設計が適切にレイアウトされている場合、通常、多くの依存関係を持つクラスはほとんどないため、手動またはフレームワークによる依存関係の結線は比較的簡単で、テストを記述する定型文の量は簡単に管理できるはずです。

ただし、DIフレームワークは必要ありません。

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

私)定型コードを避ける

プロジェクトが何度も何度も工場(およびインスタレーション)を作成する苦痛を感じるサイズになったら、DIフレームワークを使用する必要があります。

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

JavaがかつてXMLプログラミングされていたとき、現在は次のとおりです。フェアリーダストに注釈振りかける魔法が起こります。

しかし、真剣に:これには明確な利点があると思います。どこでそれを見つけるか、どのように構築するかではなく、何が必要かを伝えることで、意図を明確に伝えます。


tl; dr

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


1
あなたが正しいです!私は物事が内部でどのように機能するかについてあまりにも心配する傾向があります。私はほとんど理解していなかったフレームワークを使った作業でひどい経験をしました。私はフレームワークをテストすることを支持するのではなく、フレームワークが何をし、どのようにそれを行うかを理解するだけです(可能であれば)。しかし、それはあなた自身の興味に依存します。それらがどのように機能するかを理解すれば、あなたはそれらがあなたに適しているかどうかを判断するより良い立場にいると思います。たとえば、異なるORMを比較するのと同じ方法です。
ライヴ

1
ボンネットの下で何かがどのように機能するかを知ることは常に正しいと思います。しかし、それはあなたが知らないことを怖がらせるべきではありません。たとえば、Springのセールスポイントは、それが機能するだけだと思います。もちろん、フレームワークを使用するかどうかについて十分な情報に基づいた決定を下すには、ある程度の知識が必要です。しかし、自分と同じ傷跡を持たない人には不信感のパターンが頻繁に見られますが、あなたの場合はそうではありません。
トーマスジャンク

幸いなことに、これらの日には、SpringやDIフレームワークのJavaも必要ありません。すでにJSR330を入手しましたが、残念ながら無視されます(少なくとも、それを実装するプロジェクトは見当たりませんでした)。だから、人々はまだ傷跡を勝ち取る時間を持っている
:

回答を編集しましたので、コメントを考慮しました。デバッグに関連する「欠点」を削除しました。私はあなたがそれに正しかったという結論に達しました。現時点では、回答から削除した部分を引用するため、回答を更新することも検討してください。
ライヴ

1
答えはほとんど変わりませんでした!!! 心配する必要はありません:-)。メッセージと引数は同じままです。私はあなたの答えがそのままであることを望みました。引用符の1つを削除するだけでした。
ライヴ

0

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


0

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

  • Javaには、自動化された依存性注入を提供するいくつかの非常に優れたDIパッケージがあり、通常、単純なDIよりも手動で記述するのが難しい追加機能がパッケージ化されていますdを使用して、そのスコープの依存関係の正しいバージョンを見つけます-たとえば、アプリケーションスコープのオブジェクトは、スコープの要求を参照できます)。

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

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

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


0

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

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

このようなコードを使用すると、利益よりも害が大きくなります。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.