带有参数条件的简单工厂,使用Unity 2.0

Ulf*_*edt 10 .net c# factory dependency-injection unity-container

假设我有一个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;
    }
}
Run Code Online (Sandbox Code Playgroud)

某个客户端使用此工厂来处理包含以下条件的运行时数据:

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

public class RuntimeData
{
    public Condition Condition { get; set; }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

如何使用Unity 2.0实现相同的构造行为?
重要的部分是condition(Condition)确定如何创建和设置Product,条件只在运行时已知,并且每个MakeProduct(...)调用都不同.("其他产品设置代码"处理一些委托内容,但也可以处理其他初始化,并且需要成为构造的一部分.)

如何进行容器注册Product(或IProduct接口)?
我应该使用InjectionFactory建筑吗?我怎么做?

// How do I do this?
container.RegisterType<Product>(???)
Run Code Online (Sandbox Code Playgroud)

为了能够在客户端代码中提供条件,我需要做什么?

Naïve客户端代码(来自之前的编辑)突出显示最后一个问题,解释了几个答案的措辞:

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...
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

(我在Stackoverflow上已经阅读了很多类似的问题,但是根据我的需要,他们无法满足他们的需求.)

Chr*_*res 6

您不应该使用容器来做出这样的运行时决定.而是通过容器将工厂注入客户端.如果工厂需要容器的附属物,请在创建时将它们注入工厂.

将您的工厂更改为实际对象,而不是仅仅作为静态方法的容器,并注入它.


Wie*_*tze 6

您不应以任何方式在类中注入或使用容器.这包括使用参数.这样做的原因是,这样做会将代码绑定到容器.如果您必须实现另一个容器甚至是新版本,那么您将需要做大量的工作.

但是,对于您描述Unity(以及其他一些注入框架)的情况,该功能称为"自动工厂".它使用.NET Func <TResult>委托.这是一个.NET功能,因此它不会将您的类与Unity绑定.

这是如何使用它,首先注册您的服务.不要用a注册,ContainerControlledLifetimeManager否则每次都会获得相同的实例.

unity.RegisterType<IOpenFileService, OpenFileService>();
Run Code Online (Sandbox Code Playgroud)

然后为它注册一个自动工厂.

unity.RegisterType<Func<IOpenFileService>>();
Run Code Online (Sandbox Code Playgroud)

然后可以将其注入任何需要它的类中.

unity.RegisterType<ViewModelBase, OptionsFileLocationsViewModel>(
    new InjectionConstructor(new ResolvedParameter<Func<IOpenFileService>>());
Run Code Online (Sandbox Code Playgroud)

如果你现在解析OptionsFileLocationsViewModel它的一个实例,就不会注入一个IOpenFileService带有函数的实例,如果调用它将返回一个实例IOpenFileService.

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;
}
Run Code Online (Sandbox Code Playgroud)

我希望这个简短的解释会激励你解决你的问题.