web-dev-qa-db-ja.com

依存性注入コンテナの利点は何ですか?

依存性注入自体の利点を理解しています。たとえば、Springを見てみましょう。また、AOP、さまざまな種類のヘルパーなど、他のSpring機能の利点も理解しています。次のようなXML構成の利点は何なのでしょうか。

<bean id="Mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
  <property name="girlfriend" ref="Mary"/>
</bean>

単純な古いJavaなどのコードと比較して:

Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);

これはデバッグが容易で、コンパイル時間がチェックされ、Javaのみを知っている人なら誰でも理解できます。では、依存性注入フレームワークの主な目的は何ですか? (またはその利点を示すコードの一部。)


更新:
の場合には

IService myService;// ...
public void doSomething() {  
  myService.fetchData();
}

IoCフレームワークは、myServiceの実装が複数ある場合、どの実装を注入するのかをどのように推測できますか?特定のインターフェイスの実装が1つだけで、IoCコンテナがそれを自動的に使用することを決定した場合、2番目の実装が表示された後に壊れます。また、インターフェイスの実装が意図的に1つしかない場合は、それを挿入する必要はありません。

IoCの設定の小さな部分を見ると、それが有益であることを示すのは本当に興味深いでしょう。私はしばらくの間Springを使用していますが、そのような例を提供することはできません。そして、私が使用する休止状態、dwr、および他のフレームワークの利点を示す単一の行を示すことができます。


更新2:
再コンパイルせずにIoC構成を変更できることを認識しています。本当にそんなに良い考えですか?誰かが再コンパイルせずにDB資格情報を変更したいときは理解できます-彼は開発者ではないかもしれません。実際には、開発者以外の誰かがIoC構成を変更する頻度はどれくらいですか?開発者にとって、構成を変更する代わりにその特定のクラスを再コンパイルする努力はないと思います。そして、開発者以外の人にとっては、おそらく彼の生活を楽にし、よりシンプルな構成ファイルを提供したいと思うでしょう。


更新3:

インターフェイスとその具体的な実装間のマッピングの外部構成

それを外延にするのに何がそんなに良いのでしょうか?すべてのコードを外部にするわけではありませんが、間違いなくできます-ClassName.Java.txtファイルに配置し、その場で手動で読み取り、コンパイルするだけです-再コンパイルを避けました。コンパイルを避ける必要があるのはなぜですか?!

手続き型コードではなく宣言的にマッピングを提供するため、コーディング時間を節約できます

宣言的なアプローチが時間を節約することもあると理解しています。たとえば、BeanプロパティとDB列の間のマッピングを1回だけ宣言し、Hibernateはこのマッピングを使用して、HSQLに基づくSQLの読み込み、保存、構築などを行います。ここで宣言型アプローチが機能します。 (私の例では)Springの場合、宣言にはより多くの行があり、対応するコードと同じ表現力がありました。そのような宣言がコードより短い場合の例がある場合-私はそれを見たいと思います。

Inversion of Controlの原理により、実際の実装を偽の実装に置き換えることができるため、簡単なユニットテストが可能になります(SQLデータベースをメモリ内のものに置き換えるなど)。

制御の反転の利点を理解しています(IoCはより一般的であるため、ここで説明した設計パターンを依存性注入と呼びます-制御の種類が多く、そのうちの1つのみを反転します-初期化の制御)。なぜ誰かがプログラミング言語以外の何かを必要としているのかと尋ねていました。私は間違いなく、コードを使用して実際の実装を偽の実装に置き換えることができます。そして、このコードは設定と同じことを表現します-偽の値でフィールドを初期化します。

mary = new FakeFemale();

DIの利点を理解しています。同じことを行うコードの構成と比較して、外部XML構成によってどのような利点が追加されるのか理解できません。私はコンパイルを避けるべきだとは思わない-私は毎日コンパイルし、私はまだ生きています。 DIの構成は、宣言型アプローチの悪い例だと思います。宣言は、一度宣言され、さまざまな方法で何度も使用される場合に役立ちます-hibernate cfgのように、BeanプロパティとDB列間のマッピングが保存、ロード、検索クエリの構築などに使用されます。SpringDI設定は簡単に変換できますこの質問の冒頭のように、コードを構成することはできませんか?また、Beanの初期化にのみ使用されますよね?これは、宣言的アプローチでは何も追加されないことを意味しますか?

休止状態のマッピングを宣言するとき、休止状態にいくつかの情報を与えるだけで、それに基づいて機能します-何をすべきかを伝えません。春の場合、私の宣言は春に正確に何をすべきかを伝えます-なぜそれを宣言し、なぜそれをしないのですか?


最終更新:
みんな、たくさんの答えが依存関係の注入について教えてくれます。それは私が知っているIS GOOD。質問はコードを初期化するのではなく、DI構成の目的についてです。私がこれまでに得た唯一の答えは、構成が変更されたときに再コンパイルを回避することです。この場合は避けてください。

102
Pavel Feldman

私自身にとって、IoCを使用する(および外部構成を使用する)主な理由の1つは、次の2つの領域に関するものです。

  • テスト中
  • 生産メンテナンス

テスト

テストを3つのシナリオに分割した場合(大規模開発ではかなり正常です):

  1. 単体テスト
  2. 統合テスト
  3. ブラックボックステスト

最後の2つのテストシナリオ(統合とブラックボックス)については、アプリケーションのどの部分も再コンパイルしません。

テストシナリオのいずれかで構成の変更が必要な場合(つまり、別のコンポーネントを使用して銀行統合を模倣するか、パフォーマンスの負荷をかける)、これは簡単に処理できます(これは、DI側の構成の利点の下にあります)ただし、IoC。

さらに、アプリが複数のサイト(異なるサーバーおよびコンポーネント構成)で使用されているか、ライブ環境で構成が変更されている場合、テストの後の段階を使用して、アプリがそれらの変更を処理することを確認できます。

生産

開発者として本番環境を制御することはできません(特にすべきではありません)(特に、アプリが複数の顧客または別々のサイトに配布されている場合)。インフラストラクチャ/本番環境のサポート次第であり、開発者に戻ったりテストを行ったりすることなく、ライブ環境を調整し、ライブ環境を調整します(コンポーネントを移動するだけの場合はコストが高くなります)。

概要

IoCの外部設定が他の(開発者ではない)アプリケーションを設定する権限を与えることから得られる主な利点は、私の経験では、これは限られた状況下でのみ有用です。

  • 環境が異なる複数のサイト/クライアントにアプリケーションが配布されます。
  • 実稼働環境およびセットアップに対する開発制御/入力の制限。
  • テストシナリオ。

実際には、実行中の環境を制御できるものを開発する場合でも、時間が経つにつれて、構成を変更する機能を他の誰かに提供する方がよいことがわかりました。

  • 開発時には、いつ変更されるかわかりません(このアプリは、会社が他の人に販売しているのでとても便利です)。
  • 適切な構成モデルをセットアップして使用することで処理できたわずかな変更が要求されるたびに、コードの変更に固執したくありません。

注:アプリケーションは、完全なソリューション(実行可能ファイルだけでなく)を指すため、アプリケーションの実行に必要なすべてのファイルはです。

40

依存性注入は、オブジェクトの委任は通常、オブジェクトの継承よりも有用な設計パターンであるという観察に根ざしたコーディングスタイルです(つまり、オブジェクトの関係はオブジェクトの関係よりも有用です)。ただし、DIが機能するには、オブジェクトインターフェイスを作成するという、もう1つの要素が必要です。これら2つの強力な設計パターンを組み合わせることで、ソフトウェアエンジニアは柔軟な疎結合コードを作成できることにすぐに気付き、依存性注入の概念が生まれました。しかし、DIが本格的に普及したのは、特定の高水準言語でオブジェクトリフレクションが利用可能になるまでではありませんでした。リフレクションコンポーネントは、今日のほとんどのDIシステムの中核です。DIの非常にクールな側面は、プログラムでオブジェクトを選択し、オブジェクト自体とは独立したシステムを使用して、オブジェクトを構成および他のオブジェクトに注入する機能を必要とするためです。

言語は、オブジェクトインターフェースとオブジェクトリフレクション(Java and C#)など)のサポートだけでなく、通常のオブジェクト指向プログラミング技術の両方を適切にサポートする必要があります。C++でDIパターンを使用してプログラムを構築できます適切な言語内でのリフレクションサポートの欠如により、アプリケーションサーバーや他のDIプラットフォームのサポートが妨げられるため、DIパターンの表現力が制限されます。

DIパターンを使用して構築されたシステムの長所:

  1. DIコードは、「依存」機能が明確に定義されたインターフェイスに外挿されるため、再利用がはるかに簡単です。適切なアプリケーションプラットフォームによって構成が処理される個別のオブジェクトを他のオブジェクトに自由にプラグインできます。
  2. DIコードはテストがはるかに簡単です。オブジェクトによって表現される機能は、アプリケーションロジックが期待するインターフェイスを実装する「モック」オブジェクトを作成することにより、ブラックボックスでテストできます。
  3. DIコードはより柔軟です。それは本質的に疎結合のコードであり、極端なものです。これにより、プログラマは、一方の必要なインターフェイスと他方の表現されたインターフェイスのみに基づいて、オブジェクトの接続方法を選択できます。
  4. DIオブジェクトの外部(Xml)構成は、他のユーザーが予期しない方向でコードをカスタマイズできることを意味します。
  5. 外部設定は、オブジェクトの初期化とオブジェクトの相互依存関係の管理のすべての問題をアプリケーションサーバーで処理できるという点で、関心の分離パターンです。
  6. DIパターンを使用するために外部構成は不要であることに注意してください。単純な相互接続には、多くの場合、小さなビルダーオブジェクトで十分です。 2つの間に柔軟性のトレードオフがあります。 Builderオブジェクトは、外部から見える構成ファイルほど柔軟なオプションではありません。 DIシステムの開発者は、構成ファイルで表現されているオブジェクト構築の小規模で詳細な制御により、混乱と保守コストが将来的に増加する可能性があることに注意して、利便性に対する柔軟性の利点を検討する必要があります。

確かにDIコードはより面倒で、他のオブジェクトに注入されるオブジェクトを構成するXMLファイルをすべて持つことの欠点は難しいように見えます。ただし、これがDIシステムのポイントです。一連の構成設定としてコードオブジェクトを組み合わせて一致させることができるため、最小限のコーディングでサードパーティコードを使用して複雑なシステムを構築できます。

質問で提供される例は、適切にファクタリングされたDIオブジェクトライブラリが提供できる表現力の表面に触れているだけです。いくつかの練習と多くの自己規律により、ほとんどのDI実践者は、アプリケーションコードの100%テストカバレッジを持つシステムを構築できることに気付きます。この1点だけでも並外れています。これは、数百行のコードの小さなアプリケーションの100%テストカバレッジではなく、数十万行のコードで構成されるアプリケーションの100%テストカバレッジです。このレベルのテスト容易性を提供する他のデザインパターンを説明することができません。

ほんの数十行のコードを適用すれば、いくつかのオブジェクトと一連のXML構成ファイルよりも簡単に理解できるという点で正しいです。ただし、ほとんどの強力なデザインパターンと同様に、システムに新しい機能を追加し続けると、利点が見つかります。

要するに、大規模なDIベースのアプリケーションは、デバッグが容易であり、理解が容易です。 Xml構成は「コンパイル時チェック」ではありませんが、この作成者が認識しているすべてのアプリケーションサービスは、互換性のないインターフェイスを持つオブジェクトを別のオブジェクトに挿入しようとすると、開発者にエラーメッセージを提供します。そしてほとんどは、既知のオブジェクト構成すべてをカバーする「チェック」機能を提供します。これは、注入されるオブジェクトAが、設定されたすべてのオブジェクト注入に対してオブジェクトBに必要なインターフェイスを実装することをチェックすることにより、簡単かつ迅速に実行されます。

14
Kevin S.

これはちょっとした質問ですが、膨大な量のxml構成が実際にはあまりメリットにならないことに同意する傾向があります。私のアプリケーションは、多額のフレームワークを含め、可能な限り依存関係を軽くするのが好きです。

多くの場合、コードは単純化されますが、問題の追跡がかなり難しくなる複雑さのオーバーヘッドもあります(そのような問題を直接目にし、Java私はずっと快適です対処)。

私はそれがスタイルに少し依存していると思います、そしてあなたが何に慣れているか...あなたはあなた自身のソリューションを飛ばして、それを裏返して知る利点がありますか、または構成が難しいときに証明されるかもしれない既存のソリューションに頼るのが好きですか?ちょうどいいですか?それはすべてトレードオフです。

ただし、XMLの構成は、ちょっとした苦労の種です。

7
Mike Stone

コードをデータに変更できるときはいつでも、正しい方向に一歩進んでいます。

データとして何かをコーディングするということは、コード自体がより一般的で再利用可能であることを意味します。また、正確に適合する言語でデータを指定できることも意味します。

また、XMLファイルをGUIまたはその他のツールに読み込んで、実用的に簡単に操作できます。コード例を使用してどのようにしますか?

私はほとんどの人がコードとしてデータに実装するものを常にファクタリングしています。人々がデータとしてではなくコードでメニューを作成することは考えられません。コードでメニューを作成することは定型的な理由で明らかに間違っていることは明らかです。

5
Bill K

DIコンテナーを使用する理由は、単にゲッターとセッターであるコードで事前に構成されている10億のプロパティを持っている必要がないためです。それらをすべて新しいX()でハードコーディングしますか?もちろん、デフォルトを使用することもできますが、DIコンテナではシングルトンを作成できます。これは非常に簡単で、初期化の雑多なタスクではなく、コードの詳細に集中できます。

たとえば、Springでは、InitializingBeanインターフェースを実装し、afterPropertiesSetメソッドを追加できます(コードをSpringに結合しないようにするために「init-method」を指定することもできます)。これらのメソッドを使用すると、クラスインスタンスのフィールドとして指定されたインターフェイスが起動時に正しく構成され、ゲッターとセッターをnullチェックする必要がなくなります(シングルトンがスレッドセーフであることが許可されている場合) )。

さらに、DIコンテナを使用して複雑な初期化を自分で行うよりもはるかに簡単です。たとえば、XFire(CeltiXFireではなく、Java 1.4)のみを使用)の使用を支援します。アプリはSpringを使用しましたが、残念ながらXFireのservices.xml構成メカニズムを使用しました。 1つ以上のインスタンスではなくZERO以上のインスタンスがあることを宣言するには、この特定のサービスに提供されたXFireコードの一部をオーバーライドする必要がありました。

Spring Beanスキーマには、特定のXFireデフォルトが定義されています。したがって、Springを使用してサービスを構成していれば、Beanを使用できたはずです。その代わりに、beansを使用する代わりにservices.xmlファイルで特定のクラスのインスタンスを提供する必要がありました。これを行うには、コンストラクターを提供し、XFire構成で宣言された参照をセットアップする必要がありました。実際に変更する必要があるため、単一のクラスをオーバーロードする必要がありました。

しかし、services.xmlファイルのおかげで、コンストラクターのSpring構成ファイルのデフォルトに従ってデフォルトを設定する4つの新しいクラスを作成する必要がありました。 Spring構成を使用できた場合は、次のように言えます。

<bean id="base" parent="RootXFireBean">
    <property name="secondProperty" ref="secondBean" />
</bean>

<bean id="secondBean" parent="secondaryXFireBean">
    <property name="firstProperty" ref="thirdBean" />
</bean>

<bean id="thirdBean" parent="thirdXFireBean">
    <property name="secondProperty" ref="myNewBean" />
</bean>

<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />

代わりに、次のようになりました。

public class TheFirstPointlessClass extends SomeXFireClass {
    public TheFirstPointlessClass() {
        setFirstProperty(new TheSecondPointlessClass());
        setSecondProperty(new TheThingThatWasHereBefore());
    }
}

public class TheSecondPointlessClass extends YetAnotherXFireClass {
    public TheSecondPointlessClass() {
        setFirstProperty(TheThirdPointlessClass());
    }
}

public class TheThirdPointlessClass extends GeeAnotherXFireClass {
    public TheThirdPointlessClass() {
        setFirstProperty(new AnotherThingThatWasHereBefore());
        setSecondProperty(new WowItsActuallyTheCodeThatChanged());
    }
}

public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
    public WowItsActuallyTheCodeThatChanged() {
    }

    public overrideTheMethod(Object[] arguments) {
        //Do overridden stuff
    }
}

したがって、最終的な結果は、1つの追加クラスといくつかの単純な依存関係コンテナ情報が達成されるという影響を達成するために、4つの追加の、ほとんど無意味なJavaクラスをコードベースに追加する必要がありました。これはそうではありません。 「ルールを証明する例外」、これはISルール...プロパティがDIコンテナで既に提供されていて、それに合わせてそれらを変更している場合、コードの癖を処理することははるかにきれいです特別な状況。これは頻繁に発生します。

3
MetroidFan2002

あなたの答えがあります

それぞれのアプローチには明らかにトレードオフがありますが、外部化されたXML構成ファイルは、IDEではなくビルドシステムを使用してコードをコンパイルするエンタープライズ開発に役立ちます。ビルドシステムを使用して、特定の値をコードに挿入することができます。たとえば、ビルドのバージョン(コンパイルするたびに手動で更新しなければならない場合があります)。ビルドシステムがバージョン管理システムからコードを引き出すと、痛みはさらに大きくなります。コンパイル時に単純な値を変更するには、ファイルを変更してコミットし、コンパイルしてから、変更ごとに元に戻す必要があります。これらは、バージョン管理にコミットしたい変更ではありません。

ビルドシステムと外部設定に関するその他の便利な使用例:

  • 異なるビルドの単一のコードベースにスタイル/スタイルシートを挿入する
  • 単一のコードベースにさまざまな動的コンテンツセット(またはそれらへの参照)を挿入する
  • 異なるビルド/クライアントにローカライズコンテキストを注入する
  • webサービスURIをバックアップサーバーに変更する(メインサーバーがダウンしたとき)

更新:上記の例はすべて、必ずしもクラスへの依存を必要としないものに関するものです。しかし、複雑なオブジェクトと自動化の両方が必要なケースを簡単に構築できます-例えば:

  • あなたのウェブサイトのトラフィックを監視するシステムがあると想像してください。同時ユーザーの数に応じて、ロギングメカニズムのオン/オフを切り替えます。おそらく、メカニズムがオフになっている間、スタブオブジェクトがその場所に配置されます。
  • ユーザーの数に応じて、参加者の数に応じてP2Pを実行する機能を切り替えたいWeb会議システムがあるとします
3
badunk

構成を変更するたびにコードを再コンパイルする必要はありません。プログラムの展開と保守を簡素化します。たとえば、設定ファイルを1回変更するだけで、1つのコンポーネントを別のコンポーネントと交換できます。

2
aku

ガールフレンドの新しい実装にスロットを入れることができます。したがって、コードを再コンパイルせずに新しい女性を注入できます。

<bean id="jane" class="foo.bar.HotFemale">
  <property name="age" value="19"/>
</bean>
<bean id="mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="john" class="foo.bar.Male">
  <property name="girlfriend" ref="jane"/>
</bean>

(上記はFemaleとHotFemaleが同じGirlfFriendインターフェイスを実装することを想定しています)

2
Paul Whelan

.NETの世界では、ほとんどのIoCフレームワークがXMLとコードの両方の構成を提供します。

たとえば、StructureMapおよびNinjectは、流れるようなインターフェイスを使用してコンテナを構成します。 XML構成ファイルを使用する必要がなくなりました。 .NETにも存在するSpringは、彼の歴史的な主要な構成インターフェイスであるため、XMLファイルに大きく依存していますが、プログラムでコンテナーを構成することも可能です。

1
Romain Verdier

Springから、2つの答えがあります。

まず、構成を定義する方法はXML構成だけではありません。ほとんどのことは注釈を使用して構成でき、XMLで実行する必要があるのは、ライブラリから使用している接続プールのように、とにかく書いていないコードの構成です。 Spring 3には、Javaを使用したDI構成を定義するためのメソッドが含まれています。この例の手作業によるDI構成と同様です。Springを使用しても、XMLベースの構成ファイルを使用する必要はありません。

第二に、Springは単なるDIフレームワークではありません。トランザクション管理やAOPなど、他の多くの機能があります。 Spring XML構成は、これらすべての概念を組み合わせています。多くの場合、同じ構成ファイルで、Bean依存関係、トランザクション設定を指定し、バックグラウンドでAOPを使用して実際に処理するセッションスコープBeanを追加しています。 XML構成は、これらすべての機能を管理するためのより良い場所を提供します。また、注釈ベースの構成とXML構成は、Javaベースの構成を行うよりも優れていると感じています。

しかし、私はあなたのポイントを見て、Javaで依存性注入構成を定義することに何の問題もありません。私は通常単体テストでそれを行い、DIフレームワークを追加していないほど十分に小さなプロジェクトに取り組んでいます。私は通常、Javaで構成を指定しません。なぜなら、私にとっては、Springを使用することを選択したときに書くことから逃れようとしている親切な配管コードだからです。 'tは、XML構成がJavaベースの構成よりも優れていることを意味します。

1
David W Crook

部分的な構成の組み合わせが簡単で、最終的な完全な構成になります。

たとえば、Webアプリケーションでは、モデル、ビュー、コントローラーは通常、個別の構成ファイルで指定されます。宣言型アプローチを使用すると、たとえば次のようにロードできます。


  UI-context.xml
  Model-context.xml
  Controller-context.xml

または、別のUIといくつかの追加コントローラーをロードします。


  AlternateUI-context.xml
  Model-context.xml
  Controller-context.xml
  ControllerAdditions-context.xml

コードで同じことを行うには、部分的な構成を組み合わせるためのインフラストラクチャが必要です。コードで実行することは不可能ではありませんが、IoCフレームワークを使用する方が確かに簡単です。

1
flicken

多くの場合、重要なポイントはwhoがプログラムの作成後に構成を変更することです。コードの構成では、変更する人が元の作成者と同じスキルとソースコードなどへのアクセス権を持っていると暗黙的に仮定します。

実稼働システムでは、設定の一部のサブセット(例ではage)をXMLファイルに抽出し、システム管理者またはサポート担当者がソースコードやその他の設定よりも強力な値を与えることなく、または単に複雑さからそれらを分離するために値を変更するためにサポートします。

1
Miro A.

XML構成ファイルで初期化すると、コンピューターにアプリを展開しているクライアントとのデバッグ/適応作業が簡単になります。 (再コンパイル+バイナリファイルの置換を必要としないため)

0
Andrei Rînea

Springにはプロパティローダーもあります。このメソッドを使用して、環境に依存する変数を設定します(たとえば、開発、テスト、受け入れ、生産など)。これは、たとえば、リッスンするキューです。

プロパティが変更される理由がない場合、このように構成する理由もありません。

0
JeroenWyseur

あなたのケースは非常にシンプルであるため、SpringのようなIoC(Inversion of Control)コンテナは必要ありません。一方、「実装ではなくインターフェイスにプログラムする」場合(OOPの推奨事項)、次のようなコードを使用できます。

IService myService;
// ...
public void doSomething() {
  myService.fetchData();
}

(myServiceのタイプはIServiceであり、具体的な実装ではなくインターフェースであることに注意してください)。これで、初期化中にIoCコンテナがIServiceの正しい具体的なインスタンスを自動的に提供できるようになります。多くのインターフェイスと実装がある場合、手作業で行うのは面倒です。 IoCコンテナー(依存性注入フレームワーク)の主な利点は次のとおりです。

  • インターフェイスとその具体的な実装間のマッピングの外部構成
  • IoCコンテナーは、複雑な依存関係グラフの解決、コンポーネントの有効期間の管理など、いくつかのトリッキーな問題を処理します。
  • 手続き型コードではなく宣言的にマッピングを提供するため、コーディング時間を節約できます
  • Inversion of Controlの原理により、実際の実装を偽の実装に置き換えることができるため、簡単なユニットテストが可能になります(SQLデータベースをメモリ内のものに置き換えるなど)。
0
Borek Bernard