何时何地在运行时调用工厂?

时间:2011-10-21 22:37:18

标签: c# dependency-injection factory

我最近问过正确地做DI,并获得了一些关于它的博客文章的链接。我想我现在有了更好的理解 - 通过将它放在工厂中,将逻辑分离出来。但是所有的例子都是针对像网站这样的东西,并且说要在启动时做所有的接线。调用一个大工厂new所有的东西,并传递所有依赖项。

但如果我不想事先实例化一切呢?我有一个对象,其中包含可以委派给的其他对象的列表,但它们很昂贵,并且一次只使用一个,所以我在需要时构建它们,并在完成后让它们被收集。我不想将new B()放在A的逻辑中,因为我宁愿使用DI - 但是如何?可A致电工厂吗?除非工厂维持包括当前依赖关系的状态,否则这似乎并没有好多少。我只是不想在B构建时将A的完整列表传递给B,因为这会浪费。如果您愿意,A不一定 位于A内,尽管它具有逻辑意义(B是游戏级别,{{1} }是单个屏幕),但无论如何A的逻辑决定了何时创建B

那么,谁叫工厂获得B,何时?

澄清:我没有使用DI的框架。我想知道DI这个词是否意味着什么?

2 个答案:

答案 0 :(得分:6)

在Ninject中,您可以注册Func<B>并在构造函数中请求A

如果Func<B>已经注册,Autofac将自动提供B

或者,您可以采用更直接的方法并为B定义显式工厂,并在构造函数中请求该工厂;它只是更多的打字,因为你必须为你想懒洋洋地初始化的每个依赖创建一个工厂。


这是另一个显示Ninject样式工厂方法的答案:How do I handle classes with static methods with Ninject?


@Not使用框架:如果可以的话,我可能会考虑使用一个:IoC / DI框架通常会为您提供开箱即用的延迟创建。

如果您想继续自己动手,那么只需将创建B的工厂传递给您的A对象。或者,如果您不喜欢原始Func并且不想为所有对象创建显式工厂,那么您可以考虑使用Lazy<B>来获得更正式的解决方案。

答案 1 :(得分:1)

通常有两种模式可以使用很少需要创建的对象。 David Faivre建议,第一种模式是使用工厂。另一种是使用代理。

代理是从设计的角度来看 - 可能是最干净的解决方案,尽管它可能需要更多代码来实现。它是最干净的,因为应用程序可能完全没有意识到这一点,因为您不需要额外的接口(因为工厂方法需要)。

这是一个带有一些接口和昂贵实现的简单示例:

interface IAmAService
{
    void DoSomething();
}

class ExpensiveImpl : IAmAService
{
    private object x = [some expensive init];

    void IAmAService.DoSomething() { }
}

不,您可以基于该接口实现代理,这可能会延迟该实现的创建:

class ServiceProxy : IAmAService
{
    private readonly Func<IAmAService> factory;
    private IAmAService instance;

    public ServiceProxy(Func<IAmAService> factory)
    {
        this.factory = factory;
    }

    void IAmAService.DoSomething()
    {
        this.GetInstance().DoSomething();
    }

    private IAmAService GetInstance()
    {
        // TODO: Implement double-checked lock only a single
        // instance may exist per ServiceProxy.
        if (this.instance == null)
        {
            this.instance = this.factory();
        }

        return this.instance;
    }
}

此代理类接受工厂委托作为依赖关系,就像David Faivre在他的回答中所描述的那样,但这样应用程序不必依赖于Func<IAmAService>,而只能依赖于IAmAService }。

现在,您可以将ExpensiveImpl注入其他实例,而不是注入ServiceProxy

// Create the proxy
IAmAService service =
    new ServiceProxy(() => new ExpensiveImpl());

// Inject it into whatever you wish, such as:
var customerService = new CustomerService(service);