2009-10-01 9 views
136

私はクラスをリファクタリングし、それに新しい依存関係を追加しています。クラスは現在、コンストラクタ内に既存の依存関係を取っています。したがって、一貫性のために、コンストラクタにパラメータを追加します。
もちろん、単体テストにはいくつかのサブクラスがあります。今は、すべてのコンストラクタを一致させるように変更するというゲームをプレイしています。
私は、セッターでプロパティーを使うことは、依存関係を取得するより良い方法だと思います。私は、注入された依存関係は、クラスのインスタンスを構築するためのインターフェースの一部でなければならないとは思わない。依存関係を追加すると、すべてのユーザー(サブクラスとあなたを直接インスタンス化するすべてのユーザー)が突然それについて知ります。カプセル化が途絶えるような感じです。コンストラクタまたはプロパティセッターによる依存性注入?

これは既存のコードのパターンではありませんので、一般的なコンセンサスが何であるか、コンストラクタとプロパティの賛否両論について調べていきたいと考えています。プロパティーセッターを使用していますか?

答えて

117

まあ、それは依存します:-)。

クラスが依存関係のないジョブを実行できない場合は、コンストラクタに追加します。クラスには新しい依存関係のあるが必要です。そのため、変更を行いたいものが壊れてしまいます。また、完全に初期化されていないクラス(「2段階構成」)を作成することは、アンチパターン(IMHO)です。

クラスが依存関係なしで動作できる場合は、セッターは問題ありません。

+9

多くの場合、Nullオブジェクトパターンを使用し、コンストラクタで参照を要求することが望ましい場合が多いと思います。これにより、すべてのヌルチェックと増加した循環複雑性が回避されます。 –

+3

@マーク:良い点。しかし、問題は既存のクラスに依存関係を追加することでした。引数なしのコンストラクタを保持すると、下位互換性が得られます。 – sleske

+0

機能するために依存関係が必要なときはどうでしょうか?その依存関係のデフォルト注入で十分です。そして、その依存関係は、プロパティまたはコンストラクタのオーバーロードによって「オーバーライド可能」であるべきですか? –

6

これは主に個人的な味の問題です。 個人的に私は、実行時に実装を置き換えることができるような柔軟性を与えると信じているので、セッター注入を好む傾向があります。 さらに、多くの引数を持つコンストラクタは私の意見ではクリーンではなく、コンストラクタで提供される引数は、オプションではない引数に限定する必要があります。

クラスインターフェイス(API)がタスクを実行するために必要なものが明確である限り、 は良いです。

+0

downvotingする理由を指定してください? – nkr1pt

+3

はい、多くの引数を持つコンストラクタは悪いです。そのため、多くのコンストラクタパラメータを持つリファクタリングクラスがあります:-)。 – sleske

+10

@ nkr1pt:注入が行われないと実行時に失敗するクラスを作成することができれば、ほとんどの人(私も含めて)はセッター注入が悪いことに同意します。私は、誰かがあなたの個人的な好みであるというあなたの陳述に反対していると信じています。 – sleske

6

私はコンストラクタインジェクションを好む理由は、クラスの依存性要件を「強制」するのに役立ちます。それがc'torにある場合、コンシューマには、アプリケーションをコンパイルするためにオブジェクトを設定するためにがあります。セッター注入を使用すると、実行時まで問題がないことがわかるかもしれません。また、オブジェクトによっては、実行時間が遅くなる可能性があります。

注入されたオブジェクトに初期化のような一連の作業が必要な場合もあります。

11

多くのオプションの依存関係(すでに匂いがある)がある場合は、おそらくセッター注入が必要です。コンストラクタインジェクションはあなたの依存関係をよりよく明らかにします。

17

もちろん、コンストラクタを置くことは、すべてを一度に検証できることを意味します。読み込み専用のフィールドに物事を割り当てると、構築時にオブジェクトの依存性が保証されます。

新しい依存関係を追加するのは本当の苦痛ですが、少なくともこのようにして、コンパイラは正しいかどうか不平を言い続けます。どちらが良いことだと思います。

+0

これに加えて、これに加えて循環依存の危険性が極端に減少します... – gilgamash

6

これは最も論理的だと思われるので、私はコンストラクタを注入します。私のクラスのようなそのようなこれらの依存関係は、仕事をする必要があります。オプションの依存関係の場合、プロパティは妥当であるように見えます。

また、コンテナーを使用して作成されたプレゼンターでASP.NETビューなどの参照を持たないものを設定するためにプロパティーインジェクションを使用します。

私はそれがカプセル化を破るとは思わない。内部の作業は内部に留まらなければならず、依存関係は別の問題を処理します。

+0

あなたの答えはありがとうございますそれは確かにコンストラクタが一般的な答えだと思われます 私はそれが何らかの方法でカプセル化を破ると思います。 DIを使用すると、サブクラス(および任意の手動インスタンシエータ)は、基本クラスがどのツールを使用しているかを知ることができます。依存関係自体を使用する必要がない場合でも、すべてのサブクラスからインスタンスを継承します。 –

+0

ちょうどnice longこのサイトの例外のために答えて失った! :(要約すると、通常、ベースクラスはロジックを再利用するために使用されます。このロジックはサブクラスに入る可能性が非常に高いです...したがって、複数の外部オブジェクトに依存するベースクラスとサブクラス= –

7

私は個人的には、コンストラクターに依存関係を注入するのに比べて、 "パターン"を抽出することをお勧めします。プロパティをvirtualに設定し、派生したテスト可能クラスの実装をオーバーライドすることができます。

+1

私はこのパターンの正式名称は「テンプレートメソッド」だと思います。 – davidjnelson

11

一般的な好ましいアプローチは、できるだけコンストラクタインジェクションを使用することです。

コンストラクタインジェクションでは、オブジェクトが正しく機能するために必要な依存関係が正確に記述されています。オブジェクトを新しく作成し、依存関係が設定されていないためにメソッドを呼び出すときにクラッシュすることは厄介です。コンストラクタによって返されるオブジェクトは、動作状態にある必要があります。

コンストラクタを1つしか持たないようにしてください。設計が単純であり、あいまいさは避けられます(人にとってはDIコンテナではありません)。あなたはマーク・シーマンは、彼の著書「.NETでの依存性注入」にローカルのデフォルトを呼び出すものを持っているとき

あなたは、プロパティインジェクションを使用することができます:あなたが正常に動作し、実装を提供しますが、発信者を許可することができるので、依存関係はオプションです必要に応じて別のものを指定します。

(以下元の答え)


私は注射が必須である場合はコンストラクタ・インジェクションが優れていると思います。これによりコンストラクタが多すぎる場合は、コンストラクタの代わりにファクトリを使用することを検討してください。

注射器がオプションである場合、または途中でトラフを変更したい場合は、セッター注入が便利です。私は一般にセッターが好きではありませんが、それは味の問題です。

+0

私は、通常半分の注入を途中で変更すると主張したいと思いますあなたのオブジェクトに隠された状態を追加しているために、スタイルが悪いです。しかし、ルールには例外がありません。 – sleske

+0

そうです、なぜ私はセッターがあまり好きではないと言ったのですか...私はコンストラクタが好きですアプローチは変更できません – Philippe

+0

"コンストラクタが多すぎる場合は、コンストラクタの代わりにファクトリを使用することを検討してください。"基本的に実行時の例外を延期し、サービス・ロケーターの実装に悩まされています。 – Marco

19

与えられたクラスの依存関係を知るために、と書かれているのは、です。たとえば、データベースに接続し、永続性レイヤの依存関係を挿入する手段を提供していないクラスがある場合、ユーザーはデータベースへの接続が利用可能でなければならないことを決して知りません。しかし、コンストラクタを変更すると、パーシスタンス層に依存することをユーザーに知らせます。

古いコンストラクタを使用するたびに変更する必要がないように、古いコンストラクタと新しいコンストラクタの間に一時的なブリッジとしてコンストラクタチェーンを適用するだけです。

public class ClassExample 
{ 
    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo) 
     : this (dependnecyOne, dependencyTwo, new DependnecyThreeConcreteImpl()) 
    { } 

    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo, IDependencyThree dependencyThree) 
    { 
     // Set the properties here. 
    } 
} 

依存性注入のポイントの1つは、クラスの依存関係を明らかにすることです。クラスにあまりにも多くの依存関係がある場合、いくつかのリファクタリングが行われるはずです。クラスのすべてのメソッドがすべての依存関係を使用していますか?そうでない場合は、クラスを分割できる場所を確認するのに適しています。

+0

もちろん、コンストラクターチェインは、新しいパラメーターに合理的なデフォルト値がある場合にのみ機能します。しかし、そうでなければ、とにかく何かを壊すのを避けることはできません。 – sleske

+0

通常、依存性注入の前にメソッドで使用していたものをデフォルトのパラメータとして使用します。理想的には、これにより、クラスの動作が変更されないため、新しいコンストラクタの追加がクリーンなリファクタリングになります。 – Scott

+1

私は、データベース接続のような依存関係を管理しているリソースに関してあなたの意見を述べます。私の場合の問題は、依存関係を追加するクラスにいくつかのサブクラスがあることです。コンテナによってプロパティが設定されるIOCコンテナの世界では、セッタを使用することによって、少なくともすべてのサブクラス間のコンストラクタインターフェイスの重複に対するプレッシャーが軽減されます。 –

3

単純な単一の依存関係から複雑な複数の依存関係を構成することを検討する価値のあるオプションがあります。つまり、複合依存のための余分なクラスを定義します。これにより、WRTコンストラクタインジェクションのほうが少し難易になります。つまり、必要なすべての依存関係をインスタンス化する必要がありますが、コールごとのパラメータは少なくなります。

もちろん、依存関係のある種の論理グループがあると、化合物が任意の集約以上のものになり、1つの化合物依存関係に複数の依存関係がある場合は最も意味がありますが、 「パターン」は長年にわたって存在しており、私が見てきたものの大半はかなり自由です。

私は個人的には、依存関係やオプションなどを指定するためにメソッド/プロパティーセッターを使用するファンです。コール名は何が起こっているかを説明するのに役立ちます。ただし、この設定方法の例のスニペットを提供し、依存クラスが十分なエラーチェックを行っていることを確認することは良い考えです。設定に有限状態モデルを使用することができます。

3

私は最近、ran into a situation私はクラスに複数の依存関係がありましたが、依存関係の1つだけが必然的に各実装で変更するつもりでした。データアクセスとエラーログの依存関係はテスト目的でのみ変更される可能性が高いため、のオプションパラメータを追加して、これらの依存関係のデフォルトの実装をコンストラクタコードに提供しました。このようにして、クラスのコンシューマによってオーバーライドされない限り、クラスはデフォルトの動作を維持します。

オプションのパラメータを使用することは、.NET 4(C#とVB.NETの両方の場合、VB.NETは常にそれらを持っていましたが)など、それらをサポートするフレームワークでのみ実行できます。もちろん、クラスのコンシューマによって再割り当てできるプロパティを使用するだけで同様の機能を実現できますが、コンストラクタのパラメータにプライベートインターフェイスオブジェクトを割り当てることで、不変性を利用することはできません。

すべての消費者が提供しなければならない新しい依存関係を導入する場合は、コンストラクタとそのコンシューマのすべてのコードをリファクタリングする必要があります。上記の私の提案は、現在のすべてのコードに対してデフォルトの実装を提供できるという贅沢を持っていても、必要に応じてデフォルトの実装を上書きする機能を提供している場合にのみ、本当に適用されます。

0

どのように実装したいかによって異なります。 私は、実装に入る値が頻繁に変更されないと思うところでコンストラクタインジェクションを好む。例:compnay stragtegyがoracleサーバーに接続されている場合、コンストラクタ・インジェクションを介して接続を確立するbeanのdatasource値を設定します。 それ以外の場合、私のアプリが製品であり、顧客の任意のデータベースに接続できる可能性がある場合は、そのようなdb設定とセッター注入によるマルチブランド実装を実装します。私は例をとっただけですが、上記のシナリオを実装するためのより良い方法があります。

+1

社内でコーディングする場合でも、私は常に再配布可能なコードを開発している独立した請負業者であると考えています。私はそれがオープンソースになると思う。この方法で、私はコードがモジュール化されていてプラグ可能であり、SOLIDの原則に従っていることを確認します。 – Fred

0

コンストラクタ・インジェクションは、明示的にコードをより読みや引数をコンストラクタでチェックされた場合に未処理のランタイムエラーを起こしにくい作り、依存関係を明らかにしないが、それは本当に個人的な意見に降りてくるんし、より多くのあなたは、DIを使用しますプロジェクトによっては、より多くの場合、一方通行か他方通行の傾向があります。私は個人的には引数の長いリストを持つコンストラクタのようなコードの匂いに問題があり、オブジェクトの消費者はそのオブジェクトを使用するために依存関係を知っていなければならないと感じるので、プロパティ注入を使用する場合があります。私はプロパティインジェクションの暗黙の性質が気に入らないが、よりエレガントで洗練されたコードになっている。しかし、コンストラクタインジェクションではカプセル化レベルが高くなります。私の経験では、デフォルトのコンストラクタを避けようとしています。カプセル化されたデータの整合性に悪影響を及ぼす可能性があります。

あなたの特定のシナリオに基づいて、コンストラクタまたはプロパティでインジェクションを選択してください。それが必要と思われるだけでDIを使用しなければならないと感じたり、悪いデザインやコードの臭いを防ぐことはできません。努力と複雑さが利益を上回る場合、パターンを使用する努力に値するものではない場合もあります。単純にする。

0

これは古い記事ですが、それは将来的に必要とされている場合は、多分、これは任意の使用は次のとおりです。

https://github.com/omegamit6zeichen/prinject

私は同様の考えを持っていたし、このフレームワークを思い付きました。それはおそらく完全ではありませんが、それはプロパティの注入に焦点を当てたフレームワークのアイデアです