8

私はWebアプリケーション(主にMVC3のNinject)でIoC/DIに慣れています。私のコントローラは、私のために作成され、すべての依存関係、サブ依存関係などで埋められます。Windowsクライアント(WPF)アプリケーションでの依存性注入を行う適切な方法

しかし、シッククライアントアプリケーションでは事柄が異なります。私自身のオブジェクトを作成する必要があります。または、サービス・ロケーター・スタイルのアプローチに戻って、依存性を持つオブジェクトを私に与えるためにカーネルに(恐らくいくつかのインターフェースを介して)テストすることができます。

しかし、私はService Locatorがアンチパターンとして記述されている場所をいくつか見てきました。

私の厚いクライアントアプリでNinjectのメリットを享受したい場合は、これを得るためのより良い/より適切な方法がありますか?可能なカップリングの

  • テスト容易
  • 適切DI/IoCの
  • 最低額は

私はちょうどここMVVMについて話してビューにビューモデルを取得しておりませんのでご注意ください。これは、カーネルからリポジトリタイプのオブジェクトを提供し、そのリポジトリからフェッチされたエンティティに機能が注入されている必要があることによって特にトリガされます(もちろん、データベースのデータですが、状態に応じていくつかのオブジェクトもパラメータとして必要ですNinjectはそれをどのように提供するかを知っています)。私はどうにかして、両方のリポジトリとエンティティをuntestable messesとして残さずにこれを行うことはできますか?

不明な点がある場合は、お知らせください。ありがとう!

EDIT 7月14日

は、私が提供する2つの答えは、おそらく正しいことを確信しています。しかし、私の体のすべての繊維はこの変化と戦っています。その中には知識の不足が原因のものもありますが、このようなやり方の優雅さを見るのが難しい理由の1つがあります。

私はこれを元の質問で十分に説明していませんでしたが、いくつかの(最初は多分、おそらく後で)WPFクライアントアプリケーションで使用されるライブラリを作成しています。これらのアプリケーションはすべて同じドメインモデルなどで動作するため、DRYを維持する唯一の方法は、すべてを1つのライブラリに保存することです。しかし、このシステムの顧客が自分のクライアントを書く機会もあります。私は、彼らに話すシンプルできれいな図書館を持たせたいと思っています。私はMyCrazySystemAdapter()を新しく作成し、それを使っているのと比べて、難解なことを複雑にしているため、Composition RootでDIを使用することを強制したくありません(Mark Seemanと同じ言葉を使っています)。

ここでMyCrazySystemAdapter(私がここで私の意見に同意しないため選択した名前)は、サブコンポーネントで構成し、DIを使用して組み立てる必要があります。 MyCrazySystemAdapter自体を注入する必要はありません。これは、クライアントがシステムと通信するために使用する必要のある唯一のインターフェイスです。クライアントは喜んでそれらの1つを取得する必要があり、DIはシーンの裏にある魔法のように起こり、オブジェクトはベストプラクティスと原則を使用してさまざまなオブジェクトによって構成されます。

私はこれが物事をしたいという議論のある方法になることを認識しています。しかし、私はこのAPIのクライアントになる人も知っています。彼らがDIシステムを学び、結びつけて、アプリケーションエントリーポイント(Composition Root)でオブジェクトの構造全体を作成する必要があることがわかったら、単一のオブジェクトを新しくするのではなく、中指とデータベースを直接混乱させて、想像がつかないように物事を台無しにする。

TL; DRは:適切に構造化されたAPIを提供することは、クライアントのためのあまり面倒です。私のAPIは、DIと適切なプラクティスを使用して舞台裏で構築された単一のオブジェクトを提供する必要があります。現実の世界は、パターンや実践に真実を保つために、あらゆることを後方に構築したいという欲求よりもはるかに優れています。

答えて

5

を私はMVVMフレームワークを見てすることをお勧めCaliburnのように。それらはIoCコンテナとの統合を提供します。


基本的に、完全なアプリケーションをapp.xamlにビルドする必要があります。後で作成する必要がある部品がある場合は、起動時にそれらを作成するためのすべてをまだ知っていないため、このインスタンスを作成する必要があるクラスにインタフェース(以下を参照)またはFunc(Does Ninject support Func (auto generated factory)?を参照)として注入します。どちらもNinjectの次のリリースでネイティブにサポートされます。

public interface IFooFactory { IFoo CreateFoo(); } 
public class FooFactory : IFooFactory 
{ 
    private IKernel kernel; 
    FooFactory(IKernel kernel) 
    { 
     this.kernel = kernel; 
    } 

    public IFoo CreateFoo() 
    { 
     this.kernel.Get<IFoo>(); 
    } 
} 

ファクトリ実装は論理的にはコンテナ構成に属し、ビジネスクラスの実装には属しません。

1

私は、WPFやMVVMについて何も知りませんが、あなたの質問には、右、すべての場所でサービスロケータ(または直接の容器)を使用せずに容器の外のものを取得する方法については、基本的には?
はいの場合は、例を表示できます。

ポイントは、あなたが内部的にコンテナを使用する、代わりに工場を使用することです。この方法では、実際にコンテナを1か所だけで使用しています。

注:私は、WinFormsのに例を使用し、(私が言ったように、私はWPFを知らない、ので... 私が代わりにNInjectの城ウィンザーを使用)特定のコンテナに関連付けられていないが、あなたの基本的な質問は具体的にWPF/NInjectに縛られていないので、WFP/NInjectへの私の答えを "移植"するのは簡単なはずです。

工場は次のようになります。あなたのアプリのメインフォームは、コンストラクタ・インジェクションを経由して、このファクトリを取得します

public class Factory : IFactory 
{ 
    private readonly IContainer container; 

    public Factory(IContainer container) 
    { 
     this.container = container; 
    } 

    public T GetStuff<T>() 
    { 
     return (T)container.Resolve<T>(); 
    } 
} 

public partial class MainForm : Form 
{ 
    private readonly IFactory factory; 

    public MainForm(IFactory factory) 
    { 
     this.factory = factory; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 
    } 
} 

コンテナが初期化されるときにアプリが起動し、メインフォームが解決されます(コンストラクタインジェクションを介してファクトリを取得します)。

static class Program 
{ 
    static void Main() 
    { 
     var container = new Container(); 
     container.Register<MainForm>(); 
     container.Register<IFactory, Factory>(); 
     container.Register<IYourRepository, YourRepository>(); 

     Application.Run(container.Resolve<MainForm>()); 
    } 
} 

今、メインフォームが容器の外にあなたのリポジトリのようなものを取得するために工場を使用することができます。

var repo = this.factory.GetStuff<IYourRepository>(); 
repo.DoStuff(); 

あなたはより多くのフォームを持っているだけでなく、そこから工場を使用する場合は、メインフォームのようにこれらのフォームにファクトリを挿入し、起動時に追加のフォームを登録し、メインフォームからファクトリを使用して開きます。

これはあなたが知りたいことですか?


EDIT:
ルーベンは、もちろんあなたは正しいです。私の間違い。私の答えで
全体のものは、私がどこかに転がっていた古い例でしたが、私は私の答えを掲示し、慎重に十分な私の古い例の文脈を読んでいないとき、私は急いでいました。

私の古い例では、アプリケーションの他のフォームを開くことができ、そこからメインフォームを有する含まれていました。 それはあなたがメインフォームにコンストラクタ・インジェクションを介したおき他のフォームを注入する必要はありませんので、工場出荷時には、のために何であったかです。
代わりに、あなたはどんな新しいフォームを開くために工場を使用することができます。もちろん

var form = this.factory.GetStuff<IAnotherForm>(); 
form.Show(); 

あなたがいる限りリポジトリがフォームに渡されて、ちょうどフォームからリポジトリを取得するために工場を必要としませんコンストラクタインジェクションを介して。あなたのアプリはわずか数の形式で構成されている場合
、あなたは全く同じようにコンストラクタ・インジェクションを経由してフォームを渡すことができ、すべてで工場を必要としない:

public partial class MainForm : Form 
{ 
    private readonly IAnotherForm form; 

    // pass AnotherForm via constructor injection 
    public MainForm(IAnotherForm form) 
    { 
     this.form = form; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 
    } 

    // open AnotherForm 
    private void Button1_Click(object sender, EventArgs e) 
    { 
     this.form.Show(); 
    } 
} 

public partial class AnotherForm : Form 
{ 
    private readonly IRepository repo; 

    // pass the repository via constructor injection 
    public AnotherForm(IRepository repo) 
    { 
     this.repo= repo; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 

     // use the repository 
     this.repo.DoStuff(); 
    } 
} 
+4

-1あなたの 'GetStuff()'はドレスのサービスロケータです。これをしないでください。 (あなたの答えの残りの部分には何も問題はありませんが、SLnessはそれをirellevantにします) –

+0

Ruben、そうです。私は私の答えを修正しました! –

+0

Remoの答え(私が正しいアプローチだと思う)を見る前に、私は工場の注入を提案するつもりでした(Funcsとして適切な場合はhttp://stackoverflow.com/questions/4840157/does-ninject-support- func-auto-generated-factory/4851885#4851885) –