在我学习依赖注入(并获得第一次实践经验)的过程中,我想知道一个问题,我想到了一个我想在不久的将来解决DI的具体项目。
对于不同的分析,我想动态创建注入依赖项的对象,因为我需要任意数量的对象,这可能因用户与我的程序的交互而有所不同。我考虑将此要求作为抽象原型模式实现
public interface IAnalysis
{
SomeDataType DoSomething();
IAnalysis CreateObject();
}
从IAnalysis派生的类将负责从CreateObject()
返回该类的新对象。依赖类可以在不知道具体类型的情况下创建新对象,但仅依赖于接口,因此遵循DI的主要概念。无论如何,从IAnalysis派生的类必须使用new
关键字创建新对象。我读到在使用DI时应该避免在注射器外部创建new
的对象,因此我不太确定这是否允许"在DI。另一方面,这对我来说似乎是一个非常明智的解决方案,因为类只会创建自己的新对象,实际上不应该损害DI原则。
我认为这个概念是否明智?我可以使用其他任何解决方案来实现这一目标吗?我实际上考虑过抽象工厂,但这会伤害我的理解DI原则。
答案 0 :(得分:2)
我读到使用DI [...]时应该避免在进样器外部创建
new
的对象。
这只是部分正确。我将逐步向您展示new
有它的位置,并且使用new
来实现您的原型模式可能会很好。
让我们首先说明显而易见的:如果我们需要一个类型为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环境中实现原型模式):抽象工厂,另一种设计模式。假设我们有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());
}
}
现在你的原型模式:原型模式主要是关于对象克隆。也就是说,实现IAnalysis
接口的所有类型都必须能够创建实例的克隆(副本)。就像上面的抽象工厂示例一样,接口的唯一目的是封装某种形式的对象创建。这是它首先存在的原因,因此实现此接口的类不得将该责任委托给外部方。同样,在这种情况下使用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容器应处理哪些类型。这些所谓的依赖项或组件或服务通常被抽象为interface
或abstract class BaseClass
,因此您可以稍后将一个实现替换为另一个实现。您使用new Service(…)
的唯一位置应该在组合根中,或者(如上所示)在抽象工厂或工厂方法中(它们本身是依赖项,将被注入到您需要在何时创建对象的位置)你选择的)。如果你的代码库全部散布new Service(…)
,那么将一个实现替换为另一个实现将很困难。
但是使用new
创建原始值和值类型实例(例如string
,TimeSpan
等)是完全可以的。这些类型通常不会被DI容器实例化。
答案 1 :(得分:0)
我读到使用DI时应该避免使用构造函数,因此我是 不太确定DI中是否“允许”。
这不是它的工作方式,但构造函数是实现的实现细节,并且由于消费者只知道抽象,因此无法调用构造函数。
但这些类型需要由某人创建。这个人就是Composition Root。如果您使用pure DI,您将调用那些构造函数。如果您使用DI容器,容器将代表您调用构造函数。
记住这一点,可以使用new
关键字创建类,但是当涉及injection constructors时,您应该将此创建保留在组合根的本地。这样做的方法是在组合根中定义IAnalysis
实现 。这样,应用程序的其余部分不需要依赖于该具体类型及其构造函数。如果您使用DI库,那么该实现可以依赖于容器并调用它来请求新实例。
答案 2 :(得分:0)
我认为依赖注入的概念是,当你向另一个类注入东西时。 现在,为什么要使用依赖注入,因为OOP概念,并且类需要使用另一个类(依赖概念)。
现在,这里的注射概念意味着你应该向课堂注入一些东西。你怎么注射它?把它放在一个参数中。
您必须在接口外部创建对象,然后将对象/类作为参数传递给接口对象。
因此,您没有尝试创建服务对象,为什么不传递您需要处理的任何参数或对象,接口会为您返回结果?
将其视为私人热门公司(界面对象)的服务帮助台。 这个公司(接口对象)应该是私有的,他们不能给出他们的命中人员列表(实现类)。
作为客户,您将如何与该公司进行交易(界面对象)? 您将提供您的信息,供公司使用。 然后你会看到你想要在报纸上杀死的人,因为他们的工作(界面对象的返回值)。
谢谢,