c# - 原型(prototype)模式是否符合依赖注入(inject)?

标签 c# design-patterns dependency-injection prototype-pattern

在学习依赖注入(inject)(并获得第一次实践经验)期间,我想知道我在思考一个我想在不久的将来使用 DI 解决的具体项目时遇到的一个问题。

对于不同的分析,我想动态创建注入(inject)依赖项的对象,因为我需要任意数量的对象,这可能会因用户与我的程序的交互而变化。我考虑过将这个需求实现为抽象原型(prototype)模式

public interface IAnalysis
{
    SomeDataType DoSomething();
    IAnalysis CreateObject();
}

从 IAnalysis 派生的类将负责从 CreateObject() 返回该类的新对象。依赖类可以在不知 Prop 体类型的情况下创建新对象,而只依赖于接口(interface),因此遵守了 DI 的一个主要概念。无论如何,从 IAnalysis 派生的类必须使用 new 关键字创建新对象。我读到,使用 DI 时应避免在注入(inject)器外部使用 new 创建对象,因此我不太确定这在 DI 中是否“允许”。另一方面,这对我来说似乎是一个非常明智的解决方案,因为类只创建它们自己的新对象,这实际上不应该损害 DI 原则。

我想到的这个概念合理吗?我可以使用其他解决方案来实现此目的吗?我实际上考虑过抽象工厂,但这会损害我对 DI 原则的理解。

最佳答案

I read that creating objects with new should be avoided outside the injector when using DI […].

这只是部分正确。我将一步一步地向您展示new有它的位置,并且使用 new 可能就可以了实现您的原型(prototype)模式。

让我们首先说明显而易见的事情:如果我们需要 B 类型的实例,那么它必须由某人在某个地方创建。假设我们有这个:

class C
{
    void Baz()
    {
        B b = new B(new A(…));
        b.Bar();
    }
}

Baz需要 B为了完成它的工作。如果我们想避免new B(…) ,我们能做的最好的事情就是从代码库中的这个特定位置删除它:

class C
{
    C(Func<B> newB) // instead of Func<B>, we could also inject a B directly
    {               // (the difference being that we would no longer control
        this.newB = newB;                        // when the B gets created)
    }

    Func<B> newB;

    void Baz()
    {
        var b = newB();
        b.Bar();
    }
}

但是B被传递到C的构造函数仍然需要在某个地方创建。只是现在它在其他地方。

那么,通过避免 new 我们得到了什么? ? C不再需要了解如何准确创建 B 的内部知识.

但是 Func<B> newB 会怎样? (即工厂方法)本身创建一个 B不使用new ?看来我们不能回避new永远。

为了让大家明白这一点,让我们继续看另一个非常相关的示例,它更接近您的问题(在 DI 上下文中实现原型(prototype)模式):抽象工厂,另一种设计模式。假设我们有一个 BFactory其唯一职责是创建 B 类型的实例:

interface BFactory
{
    B CreateB();
}

我们可以在不使用 new 的情况下实现这一点吗? ?让我们以与上面相同的方式尝试:

class RedundantBFactory : BFactory
{
    RedundantBFactory(Func<B> newB)
    {
        this.newB = newB;
    }

    Func<B> newB;

    public B CreateB()
    {
        return newB();
    }
}

这绝对毫无意义!工厂存在的全部理由是它封装了有关如何创建某种类型的实例的知识。只是因为我们想避免使用 new在我们的工厂中,我们已经将这些知识具体化了,使整个工厂完全多余(因为它只是将自己的主要责任转移给另一方,而另一方必须做同等的工作)!

我们可以得出结论,使用 new 是合理且适当的。在抽象工厂和工厂方法内部(例如 BFactory 甚至上面的 newB ),如果我们不希望它们完全冗余:

class UsefulBFactory : BFactory
{
    public UsefulAFactory(Func<A> newA)
    {
        this.newA = newA;
    }

    Func<A> newA;

    public B CreateB()
    {
        return new B(newA());
    }
}

现在介绍一下原型(prototype)模式:原型(prototype)模式本质上是关于对象克隆的。也就是说,所有实现 IAnalysis 的类型接口(interface)必须能够创建实例的克隆(副本)。正如上面的抽象工厂示例一样,接口(interface)的唯一目的是封装某种形式的对象创建。这是它存在的首要原因,因此实现此接口(interface)的类一定不能将该责任委托(delegate)给外部方。再次强调,使用 new 是完全合理的。在这种情况下:

class W : IAnalysis
{
    W(X x, Y y, …)
    {
        this.x = x;
        this.y = y;
        …
    }

    public IAnalysis CreateObject()
    {
        return new W(x, y, …);
    }
}

最后一句话,只是为了强调并完成我最初的主张,即避免 new并非在所有情况下都有意义:请注意,无论如何,DI 不应该用于所有

通常,您必须决定 DI 容器应处理哪些类型。这些所谓的依赖项、组件或服务通常被抽象为 interfaceabstract class BaseClass ,以便您稍后可以用一种实现替换另一种实现。您唯一使用new Service(…)的地方应该在组合根中,或者(如上所示)在抽象工厂或工厂方法中(它们本身就是依赖项,将被注入(inject)到您需要在您选择的时间创建对象的位置)。如果您有new Service(…)大量散布在您的代码库中,很难用一种实现替换另一种实现。

但是使用 new 是完全可以的创建原始值和值类型的实例(例如 stringTimeSpan 等)。这些类型通常不会由 DI 容器实例化。

关于c# - 原型(prototype)模式是否符合依赖注入(inject)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26250858/

相关文章:

c# - 将事件变成异步调用

c# - 如何以编程方式更改图像颜色?

asp.net-core - Razor 页面中的 BasePageModel

c# - 如何在装饰器中定义事件和属性

kotlin - 什么时候适合在 Micronauts 中使用 @Singleton 和 @Prototype?

asp.net-mvc - 如何告诉 Ninject 绑定(bind)到它没有引用的实现

dependency-injection - 多个项目中的asp.net 5依赖注入(inject)

c# - 如何在 pdf 文档的所有页面上插入背景图像?

c# - 从命令行调用 Roslyn 分析器

java - 这些构建器模式之间有什么区别(如果有的话)?