10

条件パラメーターを使用してs を次のように作成する方法を決定するSimple Factory ( SimpleProductFactory)があるとします。Product

public static class SimpleProductFactory
{
    public static Product MakeProduct(Condition condition)
    {
        Product product;
        switch(condition)
        {
            case Condition.caseA:
                product = new ProductA();
                // Other product setup code
                break;
            case Condition.caseA2:
                product = new ProductA();
                // Yet other product setup code
                break;
            case Condition.caseB:
                product = new ProductB();
                // Other product setup code
                break;
        }
        return product;
    }
}

このファクトリは、次のような条件を含むランタイム データを処理するクライアントによって使用されます。

public class SomeClient
{
    // ...
    public void HandleRuntimeData(RuntimeData runtimeData)
    {
        Product product = SimpleProductFactory.MakeProduct(runtimeData.Condition);
        // use product...
    }
    // ...
}

public class RuntimeData
{
    public Condition Condition { get; set; }
    // ...
}

Unity 2.0 を使用して同じ構築動作を実現するにはどうすればよいですか?
重要な部分は、条件 ( Condition) によって の作成方法とセットアップ方法が決定されることと、条件が実行時にのみ認識され、呼び出しProductごとに異なることです。MakeProduct(...)(「その他の製品セットアップ コード」は、一部のデリゲートを処理しますが、他の初期化も処理する可能性があり、構築の一部である必要があります。)

Product(または IProduct インターフェイス)のコンテナー登録はどのように行う必要がありますか? コンストラクション
を使用する必要がありますか?InjectionFactoryそれ、どうやったら出来るの?

// How do I do this?
container.RegisterType<Product>(???)

クライアント コードで条件を指定できるようにするには、どうすればよいですか?

いくつかの回答の文言を説明する最後の質問を強調する単純なクライアント コード (以前の編集から):

public class SomeClient
{
    // ...
    public void HandleRuntimeData(RuntimeData runtimeData)
    {
        // I would like to do something like this,
        // where the runtimeData.Condition determines the product setup.
        // (Note that using the container like this isn't DI...)
        Product product = container.Resolve<Product>(runtimeData.Condition);
        // use product...
    }
    // ...
}

(ここStackoverflowで同様の質問をたくさん読みましたが、それらとその回答を私のニーズに合わせることができませんでした。)

4

4 に答える 4

6

このような実行時の決定を行うためにコンテナを使用しないでください。代わりに、コンテナを介してファクトリをクライアントに注入します。工場がコンテナからの依存性を必要とする場合は、作成時にそれらを工場に注入します。

ファクトリを静的メソッドの単なるコンテナではなく実際のオブジェクトに変更し、それを注入します。

于 2013-01-28T20:23:25.353 に答える
6

コンテナをクラスに注入したり使用したりしないでください。これには、パラメーターの使用が含まれます。これは、コードがコンテナーにバインドされるためです。別のコンテナや新しいバージョンを実装する必要がある場合、多くの作業が残されます。

ただし、説明している Unity (および他のいくつかのインジェクション フレームワーク) には、「自動ファクトリ」と呼ばれる機能があります。.NET Func<TResult> デリゲートを使用します。これは .NET の機能であるため、クラスを Unity に関連付けることはありません。

使い方はこんな感じで、まずは自分のサービスを登録します。で登録しないでください。登録すると、ContainerControlledLifetimeManager毎回同じインスタンスが取得されます。

unity.RegisterType<IOpenFileService, OpenFileService>();

次に、自動工場を登録します。

unity.RegisterType<Func<IOpenFileService>>();

これは、それを必要とする任意のクラスに注入できます。

unity.RegisterType<ViewModelBase, OptionsFileLocationsViewModel>(
    new InjectionConstructor(new ResolvedParameter<Func<IOpenFileService>>());

のインスタンスを解決すると、 のインスタンスではなく、呼び出された場合に のインスタンスを返す関数がOptionsFileLocationsViewModel注入されます。IOpenFileServiceIOpenFileService

private readonly Func<IOpenFileService> openFileServiceFactory;

private string SelectFile(string initialDirectory)
{
    var openFileService = this.openFileServiceFactory();
    if (Directory.Exists(initialDirectory))
    {
        openFileService.InitialDirectory = initialDirectory;
    }
    else
    {
        openFileService.InitialDirectory =
            System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop);
    }

    bool? result = openFileService.ShowDialog();
    if (result.HasValue && result.Value)
    {
        return openFileService.FileName;
    }

    return null;
}

この私の簡単な説明が、あなたの問題を解決するきっかけになることを願っています。

于 2013-01-30T10:32:04.013 に答える
3

登録に一意の名前を定義できます。

container.RegisterType<Product ,ProductA>("ProductA");
container.RegisterType<Product, ProductB>("ProductB");

または構成ファイル内。

<register type="Product" mapTo="ProductA" name="ProductA" />
<register type="Product" mapTo="ProductB" name="ProductB" />

次に、登録に基づいてインスタンスを解決できます。

string productName = "ProductB";
Product product = container.Resolve<Product>(productName);

名前に次のようなクラスを使用することもできます。

public class ProductTypes
{
    public static string ProductA
    {
        get
        {
            return "ProductA";
        }
    }

    public static string ProductB
    {
        get
        {
            return "ProductB";
        }
    }
}

それから;

container.RegisterType<Product,ProductA>(ProductTypes.ProductA);
container.RegisterType<Product,ProductB>(ProductTypes.ProductB);

そしてそれを解決します。

Product product = null;

switch(condition)
{
    case Condition.caseA:
        product = container.Resolve<Product>(ProductTypes.ProductA);
        // Other product setup code
        break;
    case Condition.caseA2:
        product = container.Resolve<Product>(ProductTypes.ProductA2);
        // Yet other product setup code
        break;
    case Condition.caseB:
        product = container.Resolve<Product>(ProductTypes.ProductB);
        // Other product setup code
        break;
}

return product;
于 2013-01-28T13:55:40.130 に答える
2

@ChrisTavares が説明し、この回答で説明されているように、解決策は単にファクトリを client に注入することですSomeClient。さらに、Dependency Inversion Principle (DIP)に従うために、クライアントはファクトリ インターフェイスなどの抽象ファクトリのみに依存する必要がありますIProductFactory

実際には、単純な依存性注入 (DI) を実行するだけの問題です。Unity の使用は、(コンストラクターの) 依存関係の解決を処理するための DI ファシリテーターとしてのみ使用されます。ProductFactoryユニティ コンテナに登録する必要があるのは、コンクリート ファクトリだけです。製品の作成は工場で完全に処理され、'new' キーワードを使用しても問題ありません。

container.RegisterType<IProductFactory, ProductFactory>();

ソリューションは次のようになります。

public interface IProductFactory
{
    IProduct MakeProduct(Condition condition);
}

internal class ProductFactory : IProductFactory
{
    public IProduct MakeProduct(Condition condition)
    {
        IProduct product;
        switch (condition)
        {
            case Condition.CaseA:
                product = new ProductA();
                // Other product setup code
                break;
            case Condition.CaseA2:
                product = new ProductA();
                // Yet other product setup code
                break;
            case Condition.CaseB:
                product = new ProductB();
                // Other product setup code
                break;
            default:
                throw new Exception(string.Format("Condition {0} ...", condition));
        }
        return product;
    }
}

public class SomeClient
{
    private readonly IProductFactory _productFactory;

    public SomeClient(IProductFactory productFactory) // <-- The factory is injected here!
    {
        _productFactory = productFactory;
    }

    // ...
    public void HandleRuntimeData(RuntimeData runtimeData)
    {
        IProduct product = _productFactory.MakeProduct(runtimeData.Condition);
        // use product...
    }
    // ...
}

public class RuntimeData
{
    public Condition Condition { get; set; }
    // ...
}

public interface IProduct
{ //...
}
internal class ProductB : IProduct
{ //...
}
internal class ProductA : IProduct
{ //...
}
public enum Condition { CaseA, CaseA2, CaseB }
于 2013-10-04T13:43:05.873 に答える