Factoryパターンと組み合わせてDependency Injectionを使用する方法


11

任意のタイプのファイルの解析を担当するモジュールを検討してください。ここですでに説明したように、この問題に取り組むために戦略パターンを使用することを考えています。この質問に進む前に、リンクされた投稿を参照してください。

product.xmlファイルのコンテンツを必要とするクラスBを検討してください。このクラスは、XMLファイルを解析するために、Parserインターフェースの適切な具象実装者をインスタンス化する必要があります。適切な具象実装者のインスタンス化をファクトリに委任して、クラスBが「has-a」ファクトリになるようにすることができます。ただし、クラスBは、具体的な実装者をインスタンス化するためにファクトリに「依存」します。これは、クラスBのコンストラクターまたはセッターメソッドをFactoryに渡す必要があることを意味します。

したがって、ファイルを解析する必要があるファクトリとクラスBは、互いに密接に結合されます。私はこれまで説明してきたことについて完全に間違っている可能性があることを理解しています。私は、注入される依存関係がファクトリであるシナリオで依存関係の注入を使用できるかどうか、そしてこれを実装する正しい方法は何であるかを知りたいので、ユニットテストでファクトリをモックするなどの領域を活用できます。

回答:


8

これを行う正しい方法は、インターフェイスに依存し、そのインターフェイスの実装をクラスBに挿入することです。

インターフェースは、あなたが頼ることができる最も薄いものにすぎません-私はそれをほんの少しの煙をつかもうとすることに例えます。コードは何かに結合する必要があります。さもなければ何もしませんが、インターフェースへの結合はできる限り分離されていますが、インターフェースは必要な機能をすべて提供できます。

したがって、クラスBのコンストラクターが必要なクラスへのインターフェースを取得し、そのクラスをインターフェースの実装者としてファクトリに生成させます。ファクトリに依存せず、インターフェイスに依存し、ファクトリにそのファクトリの実装を提供します。

そのため、依存性注入を使用しますが、それは何の問題もありません。依存性注入(特に単純なコンストラクター注入)は、「通常の」方法で行う必要があります。アプリ内でmainできるだけ新しい(そしての最初の行に近い)新しいものを先送りし、ものを作成するために特別に設計されたクラスで新しい呼び出しを非表示にします。

結論:依存関係を注入することをためらわないでください。それは物事を行う通常の方法でなければなりません。


コンストラクター注入は、可能な限り新しいものを先送りしますか?
JeffO

それは悪評だった-私はそれを修正して、「可能な限りあなたのアプリに遡る」と言った。
ニックホッジズ

はい。ファクトリーではなくパーサーインターフェイスに依存することを考えていました。別のクラスがクラスBを使用する場合、クラスBが依存するインターフェイスに依存する必要があります。これは、トップレベルのクラスまでずっと続きます。この最上位クラスは、最終的にファクトリを使用して、パーサーの適切な具体的なインスタンスをインスタンス化する必要があります。最上位クラスはファクトリに依存するべきですか、それともそのメソッド内で直接ファクトリを使用する必要がありますか?
CKing

1
よく言った:依存関係を注入することを
he

9

あなたの前提はここでは少し混乱していると思います、あなたはファクトリを注入することについて話しますが、ファクトリパターンは、DIフレームワークが普及していないときに依存性注入フレームワークが行うことのサブセットを行うことを目的とした創造的なパターンですそのため便利です。ただし、DIフレームワークがある場合、DIフレームワークはファクトリが満たすはずの目的を果たすことができるため、実際にはファクトリは必要ありません。

そうは言っても、依存関係の注入と、一般的な使用方法について少し説明しましょう。

依存性注入を行うにはさまざまな方法がありますが、最も一般的なのはコンストラクター注入、プロパティ注入、および直接DIContainerです。プロパティインジェクションはほとんどの場合間違ったアプローチであるため(時々正しいアプローチです)、コンストラクターインジェクションについてお話します。DIContainerアクセス​​は、他のアプローチのいずれかを絶対に行えない場合を除いて好ましくありません。

コンストラクターインジェクションは、依存関係のインターフェイスと、その依存関係の具体的な実装を知っているDIContainer(またはファクトリー)があり、そのインターフェイスに依存するオブジェクトが必要な場合は、構築時にファクトリーから実装を渡します。それ。

すなわち

IDbConnectionProvider connProvider = DIContainer.Get<IDbConnectionProvider>();
IUserRepository userRepo = new UserRepository(connProvider);
User currentUser = userRepo.GetCurrentUser();

多くのDIフレームワークは、DIContainerがUserRepositoryのコンストラクターで具体的な実装を知っているインターフェイスを検査し、それらを自動的に渡す場所まで、これを大幅に簡素化できます。この手法は頻繁にInversion of Controlと呼ばれますが、DIとIoCはどちらも頻繁に交換される用語であり、あいまいな(ある場合)違いがあります。

ここで、包括的なコードがDIContainerにアクセスする方法を知りたい場合は、アクセスするための静的クラスを使用するか、ほとんどのDIフレームワークでDIContainerを更新することができます。指定されたインターフェースに対して具体的であるとわかっている型の内部シングルトン辞書へのラッパー。

つまり、コード内の任意の場所でDIContainerを更新し、インターフェイスとコンクリートの関係を知るために既に設定したものと同じDIContainerを効果的に取得できます。DIContainerを直接操作してはならないコードの部分からDIContainerを隠す通常の方法は、必要なプロジェクトにのみDIフレームワークへの参照があるようにすることです。


最初の段落には完全に同意しません。DIコンテナーによってインジェクトされるファクトリー(インターフェイスの実装)に依存するシナリオがまだあります。
ピョートルペラ

OPはDIフレームワークやDIコンテナについて話していないので、あなたはその点を見逃したと思います。
ドックブラウン

@Jimmy Hoffaあなたはいくつかの非常に興味深い点を指摘しましたが、SpringやDependency InjectionのcconceptなどのIoCコンテナーを知っています。私の懸念は、IoCフレームワークを使用しないシナリオに関するものでした。その場合、依存関係をインスタンス化するクラスを作成する必要があります。クラスAがインターフェイスBに依存し、クラスCがクラスAを使用する場合、クラスCはインターフェイスBに依存します。誰かがクラスCにクラスB ceを与える必要があります。クラスCはその後
claに

クラスAがインターフェイスBに依存し、クラスCがクラスAを使用する場合、クラスCはインターフェイスBに依存します。クラスCにインターフェイスBを与える必要があります。クラスCがインターフェイスBに依存するか、インスタンス化したクラスに依存する必要があります依存関係、つまり工場。あなたはあなたの答えでこの質問に答えたようですが、情報の過負荷で:)
CKing

@bot私が大部分で使用すると、DIコンテナの機能のサブセットとして工場を扱うことができ、序文で述べたように、それはドク・ブラウンが述べたように、すべてのシナリオでは真実ではないのですが、私のサンプルコードを見て交換DIContainerしてDbConnectionFactoryとコンセプトそれでも、DI / Factory / etcから具体的な実装を取得し、構築時にそのタイプのコンシューマーに渡すということです。
ジミー・ホッファ

5

他の何かを渡すのと同じように、依存性注入を介してファクトリーを渡すことができます。状況の再帰性が混乱を招かないようにしてください。私はそれを実装することについて他に何を言うべきかわかりません-あなたはすでに依存性注入を行う方法を知っています。

DIを使用して、工場にかなり定期的に注入します。


1
クラスがファクトリに依存している場合、そのクラスを単体テストするときにファクトリをモックする必要があります。それをどうやってやるの?クラスをファクトリインターフェースに依存させますか?
CKing

4

工場への注入に問題はありません。「親」オブジェクトのビルド中に必要な依存関係の種類を決定できない場合は、標準的な方法です。例がそれを最もよく説明すると思います。Javaがわからないので、c#を使用します。


class Parent
{
    private IParserFactory parserFactory;
    public Parent(IParserFactory factory)
    {
        parserFactory = factory
    }

    public ParsedObject ParseFrom(string filename, FileType fileType)
    {
        var parser = parserFactory.CreateFor(fileType);
        return parser.Parse(filename);
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.