【问题标题】:Can't combine Factory / DI无法结合 Factory / DI
【发布时间】:2026-01-27 07:15:01
【问题描述】:

假设我有一些类 Foo,它有两个依赖项:ISerializer<T>IFileAccessHandler

现在这个类还有其他的依赖,函数依赖。我不希望任何人在无效状态下实例化此类,因此我还需要在构造函数中传递域对象。

但是,当我也知道在实际创建类 Foo 时要传递什么域对象时,如何让 IoC 处理呢?

我将域对象设置为工厂设置的属性。因此,Factory 调用 Service Locator 以获取正确实例化的“Foo”类及其依赖项,并进一步用正确的域对象填充它并返回它。

但这是最好的方法吗?我宁愿让我的构造函数中包含域对象部分,以使其变得明显,您实际上需要使用“Foo”。

有什么想法吗? 我在这里遗漏了什么吗?

【问题讨论】:

    标签: design-patterns dependency-injection


    【解决方案1】:

    当您在注册时无法连接具体类型时,DI 的默认解决方案是使用抽象工厂

    在你的情况下,我会定义一个 IFooFactory 接口:

    public interface IFooFactory
    {
        Foo Create(DomainClass dc);
    }
    

    这将允许您定义一个了解您的基础架构服务的具体实现。

    public class FooFactory : IFooFactory
    {
        private readonly ISerializer serializer;
        private readonly IFileAccessHandler fileHandler;
    
        public FooFactory(ISerializer serializer, IFileAccessHandler fileHandler)
        {
            if(serializer == null)
            {
                throw new ArgumentNullException("serializer");
            }
            if(fileHandler == null)
            {
                throw new ArgumentNullException("fileHandler");
            }
    
            this.serializer = serializer;
            this.fileHandler = fileHandler;
        }
    
        public Foo Create(DomainClass dc)
        {
            return new Foo(this.serializer, this.fileHandler, dc);
        }
    }
    

    通过这种方式,您可以保护 Foo 类的不变量,使您能够继续使用 构造函数注入

    在 DI 容器中,可以注册 IFooFactory 和对应的实现。只要你有一个 DomainClass 实例并且需要一个 Foo 实例,你就会依赖 IFooFactory 并使用它。

    【讨论】:

    • 因为那将是服务定位器反模式 (blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx)。 DI 容器不得侵入应用程序的其余部分。但是,某些容器(例如 Windsor)可以自动实现和发出抽象工厂的实现,在这种情况下 FooFactory 将是完全多余的。如果你想使用 DI Container,一路走下去:)
    • 我同意服务定位器是反模式的,但每个规则都有例外。你不应该盲目地遵循这个规则!您手动连接对象,您的抽象工厂具有不正确的依赖关系。这是一种糟糕的设计,不是吗? DI 容器旨在消除这种气味,不是吗?所以使用 DI 容器来创建 Foo!你说得对 :) 如果你想使用 DI Container,就一路走下去 :)
    • 动态工厂除了温莎,为什么 FooFactory 会有不正确的依赖关系?它仍然使用构造函数注入,因此您仍然需要 DI 容器为您连接它。关键是你不能创建没有 DomainClass 的 Foo,它只在运行时可用。将 DI 容器用作服务定位器不会改变这一点,但会增加与 DI 容器的紧密耦合,以及与 Foo 的逻辑耦合。这只会让你比上面的实现更糟糕。
    • 抽象工厂能否返回具体类型? Foo 这里是一个具体的类型,你可以新建它。我认为抽象工厂是为了只返回抽象?还是我错了?
    • @GetFuzzy 抽象工厂也是一个“新事物”,但也许这会有所帮助:blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple 另见my book 的第 6 章。这是基于运行时值选择依赖项的另一种方法:*.com/a/22705794/126014
    【解决方案2】:

    我也在努力解决这个问题。 Mark 的示例受到限制,因为 FooFactory 正在创建一个具体的类 Foo。如果要创建一个在启动配置期间确定实现的 IFoo 怎么办?这意味着对于 IFoo(FooA、FooB 等)的每个替代实现,您都需要相应工厂(FooAFactory、FooBFactory 等)的具体实现。这让我觉得多余。

    如果工厂定义在与Container的实现和初始化相同的级别,我看不出工厂引用容器的弱点。它仍然可以防止容器的引用泄漏到应用程序的其余部分。

    最好的问候,

    地铁。

    【讨论】:

    • 有一篇文章同意这个观点(mark^ 写的)但是每个消费者都需要实现他们自己的工厂实现。 (只是在您的帖子中添加一个不好或不好的内容)[blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory/]
    • @Metro 如果我没有正确阅读您的建议,请纠正我。所以你的建议是让工厂使用容器解析抽象类型?所以本质上是服务定位器?但是由于工厂的实现与 IoC 容器处于同一级别,因此调用 Service Locator 并不会伤害您的灵魂?
    最近更新 更多