依存関係注入について学んでいるだけで、何かに行き詰まっています。 Dependency Injectionは、コンストラクターを介して依存クラスを送信することをお勧めしますが、これがデータオブジェクトに必要かどうかは疑問です。単体テスト機能はDIの主な利点の1つであるため、データを格納するだけで、手順が単体テストされることのないデータオブジェクトであり、DIは不必要な複雑なレイヤーになり、依存関係の表示にも役立ちますデータオブジェクトとは?
Class DO{
DO(){
DataObject2List = new List<DO2>();
}
public string Field1;
public string Field2;
public List<DO2> DataObject2List;
}
Class DO2{
public DateTime Date;
public double Value;
}
ここで使用しているいくつかの用語、具体的には「依存関係」と「依存関係注入」の明確化を提案したいと思います。
依存:
「依存関係」は通常、別のクラスが依存する必要のある機能を実行する複雑なオブジェクトです。いくつかの古典的な例は、ロガー、データベースアクセサー、または特定のビジネスロジックを処理するコンポーネントです。
DTOや値オブジェクトなどのデータのみオブジェクトは、必要な機能を実行しないため、通常「依存関係」と呼ばれません。
このように見たら、あなたの例(作曲DO
オブジェクトとD02
オブジェクトのリストをコンストラクタに通して)]は考慮しないでください。 「依存症注射」なんて。プロパティを設定するだけです。コンストラクタで提供するか他の方法で提供するかはあなた次第ですが、コンストラクタを介して単に渡すだけでは、依存性注入は行われません。
依存性注入:
DO2
クラスが実際にDO
クラスに必要ないくつかの追加機能を提供している場合、それは本当に依存関係になります。その場合、依存クラスDO
はインターフェイス(ILoggerやIDataAccessorなど)に依存し、呼び出し側のコードに依存してそのインターフェイスを提供します(つまり、インターフェイスに「挿入」しますDO
インスタンス)。
このような方法で依存関係を注入すると、DO
オブジェクトへのインターフェイスの独自の実装をそれぞれのコンテキストで提供できるため、DO
オブジェクトがより柔軟になります。 (単体テストを考えてください。)
問題の混乱をなくすために最善を尽くします。
まず、「データオブジェクト」は意味のある用語ではありません。このオブジェクトのonly定義の特性が、それがメソッドを持たないことである場合、それはまったく存在してはなりませんです。動作のない有用なオブジェクトは、次のサブカテゴリの少なくとも1つに適合する必要があります。
値オブジェクト または「レコード」にはまったく同一性がありません。それらは値typesである必要があり、環境がそれをサポートしていると仮定して、コピーオンリファレンスセマンティクスを使用します。これらは固定構造であるため、VOはプリミティブタイプまたはプリミティブの固定シーケンスでなければなりません。したがって、VOには依存関係があってはなりませんorアソシエーション。デフォルト以外のコンストラクタは、値を初期化する目的でのみ存在します。これは、リテラルとして表現できないためです。
データ転送オブジェクト は、値オブジェクトと誤って混同されることがよくあります。 DTO doはアイデンティティを持っているか、少なくとも--canです。 DTOの唯一の目的は、あるドメインから別のドメインへの情報の流れを促進することです。彼らには決して「依存関係」はありません。 may関連付け(つまり、配列またはコレクション)がありますが、ほとんどの人はそれらをフラットにすることを好みます。基本的に、これらはデータベースクエリの出力の行に類似しています。これらは通常永続化またはシリアル化する必要のある一時的なオブジェクトであり、したがって、できない抽象型を参照すると、使用できなくなります。
最後に、 データアクセスオブジェクト は、ある種のデータベースにラッパーまたはファサードを提供します。これらには明らかに依存関係があります-データベース接続や永続化コンポーネントに依存しています。ただし、それらの依存関係はほとんどの場合外部で管理され、呼び出し元にはまったく見えません。 Active Record パターンでは、構成を通じてすべてを管理するフレームワークです。古い(今日の標準では古代)DAOモデルでは、これらをコンテナー経由でしか構築できませんでした。これらのいずれかをコンストラクターインジェクションで見た場合、非常に心配になります。
エンティティオブジェクトまたは "ビジネスオブジェクト" と考えることもできます。この場合、do依存性注入をサポートしたいが、あなたが考える方法またはあなたが考える理由ではない。 ser codeの利点ではありません。エンティティマネージャまたはORMの利点であり、暗黙的にproxyを挿入して、クエリの理解などの空想的なことを行います。または遅延読み込み。
これらでは、通常しないインジェクション用のコンストラクタを提供します。代わりに、プロパティを仮想化して抽象型を使用するだけです(例:IList<T>
の代わりに List<T>
)。残りは舞台裏で起こり、誰も賢明ではありません。
つまり、全体として、「データオブジェクト」に適用されている目に見えるDIパターンは不要であり、おそらく赤信号でさえあります。しかし、それは主に、データベースのデータを表すために特別に使用されている場合を除いて、オブジェクトの存在が赤旗であるためです。他のほとんどすべての場合、それはコードのにおいであり、通常は Anemic Domain Model の始まり、または少なくとも Poltergeist です。
繰り返します:
フィン。
あなたの例では、DO
には機能的な依存関係はありません(基本的に何もしないため)。具象型DO2
に依存するため、抽象DO2
へのインターフェースを導入して、コンシューマが子クラスの独自の具象実装を実装できるようにすることができます。
本当に、ここでどんな依存関係を注入しますか?
これはデータアクセス層のデータオブジェクトであるため、データベースサービスに直接依存する必要があります。 DatabaseServiceをコンストラクターに指定できます。
DataObject dataObject = new DataObject(new DatabaseService());
dataObject.Update();
ただし、注入はコンストラクター内にある必要はありません。または、各CRUDメソッドを介して依存関係を提供することもできます。実際に永続化する必要があるまで、データオブジェクトはどこに永続化するかを知る必要がないため、このメソッドを以前よりも優先します。
DataObject dataObject = new DataObject();
dataObject.Update(new DatabaseService());
あなたは間違いなくnot CRUDメソッドで構造を隠したい!
public void Update()
{
// DON'T DO THIS!
using (DatabaseService dbService = new DatabaseService())
{
...
}
}
別のオプションは、オーバーライド可能なクラスメソッドを介してDatabaseServiceを構築することです。
public void Update()
{
// GetDatabaseService() is protected virtual, so in unit testing
// you can subclass the Data Object and return your own
// MockDatabaseService.
using (DatabaseService dbService = GetDatabaseService())
{
...
}
}
最後の選択肢は、シングルトンスタイルのServiceLocatorを使用することです。このオプションは嫌いですが、単体テストは可能です。
public void Update()
{
// The ServiceLocator would not be a real singleton. It would have a setter
// property so that unit tests can swap it out with a mock implementation
// for unit tests.
using (DatabaseService dbService = ServiceLocator.GetDatabaseService())
{
...
}
}