8

私はWebアプリケーションのIoC/DIに慣れています-主にMVC3でNinjectします。私のコントローラーは私のために作成され、すべての依存関係、サブ依存関係などが入力されています。

ただし、シッククライアントアプリケーションでは状況が異なります。独自のオブジェクトを作成するか、サービスロケータースタイルのアプローチに戻して、依存関係のあるオブジェクトを提供するようにカーネルに依頼する必要があります(おそらく、テスト容易性を考慮して、何らかのインターフェイスを介して)。

ただし、ServiceLocatorがアンチパターンとして説明されている場所をいくつか見ました。

だから私の質問は-シッククライアントアプリでNinjectの恩恵を受けたいのなら、これをすべて手に入れるためのより良い/より適切な方法はありますか?

  • 妥当性
  • 適切なDI/IoC
  • 可能な限り最小のカップリング

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

不明な点がありましたらお知らせください。ありがとう!

7月14日編集

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

元の質問ではこれについて十分に説明していませんでしたが、いくつかの(最初は4〜5、おそらくもっと後で)WPFクライアントアプリケーションで使用されるライブラリを作成しているということです。これらのアプリケーションはすべて同じドメインモデルなどで動作するため、すべてを1つのライブラリに保持することがDRYを維持する唯一の方法です。ただし、このシステムの顧客が独自のクライアントを作成する可能性もあります。そして、私は彼らに、話しかけるためのシンプルでクリーンなライブラリを用意してもらいたいと思っています。私は彼らにコンポジションルートでDIを使用するように強制したくありません(彼の本でMark Seemanのような用語を使用しています)-それはMyCrazySystemAdapter()を新しくしてそれを使用するだけの場合と比較して非常に複雑になるためです。

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

これは物事をやりたいという物議を醸す方法になるだろうと私は理解しています。ただし、このAPIのクライアントになる人も知っています。DIシステムを学習して接続し、アプリケーションのエントリポイント(構成ルート)でオブジェクト構造全体を事前に作成する必要があることがわかった場合、単一のオブジェクトを作成する代わりに、中指とデータベースを直接いじって、想像もできないような方法で物事を台無しにしてください。

TL; DR:適切に構造化されたAPIを提供することは、クライアントにとって非常に面倒です。私のAPIは、DIと適切なプラクティスを使用して舞台裏で構築された単一のオブジェクトを提供する必要があります。現実の世界では、パターンや慣習に忠実であり続けるために、すべてを逆方向に構築したいという願望に勝る場合があります。

4

2 に答える 2

5

CaliburnのようなMVVMフレームワークを確認することをお勧めします。これらは、IoCコンテナーとの統合を提供します。


基本的に、app.xamlで完全なアプリケーションを構築する必要があります。起動時に作成するすべてのものがまだわからないために後で作成する必要があるパーツがある場合は、インターフェイス(以下を参照)またはFunc(NinjectがFunc(自動生成ファクトリ)をサポートしていますか?を参照)としてファクトリをクラスに挿入します。このインスタンスを作成する必要があります。どちらも、次の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>();
    }
}

ファクトリ実装は、ビジネスクラスの実装ではなく、コンテナ構成に論理的に属していることに注意してください。

于 2011-07-10T22:59:04.800 に答える
1

WPFやMVVMについては何も知りませんが、基本的には、サービスロケーター(またはコンテナーを直接)を使用せずにコンテナーからデータを取り出す方法について質問します。
はいの場合、例を示します。

重要なのは、代わりにファクトリを使用することです。ファクトリは内部でコンテナを使用します。このように、実際には1か所でのみコンテナを使用しています。

注:WinFormsを使用し、特定のコンテナーに関連付けられていない例を使用します(前述のとおり、WPFがわからないため、NInjectの代わりにCastle Windsorを使用します)が、基本的な質問は具体的にはありません 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();

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

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


編集:
ルーベン、もちろんあなたは正しい。私の間違い。
私の答えのすべては私がどこかに横たわっていた古い例でした、しかし私が私の答えを投稿したとき私は急いでいて、私の古い例の文脈を十分に注意深く読んでいませんでした。

私の古い例には、アプリケーションの他のフォームを開くことができるメインフォームがあることが含まれていました。これがファクトリの目的であるため、コンストラクターインジェクションを介して他のすべてのフォームをメインフォームにインジェクトする必要はありません。
代わりに、ファクトリを使用して新しいフォームを開くことができます。

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();
    }
}
于 2011-07-09T12:46:54.600 に答える