每次有人联系我,并要求我以概念性方式定义“依赖项注入”,并解释在软件设计中使用DI的真正利弊。我承认我很难解释DI的概念。每次我需要告诉他们有关单一责任原则,组成而不是继承的历史时。
任何人都可以帮助我解释为开发人员描述DI的最佳方法吗?
每次有人联系我,并要求我以概念性方式定义“依赖项注入”,并解释在软件设计中使用DI的真正利弊。我承认我很难解释DI的概念。每次我需要告诉他们有关单一责任原则,组成而不是继承的历史时。
任何人都可以帮助我解释为开发人员描述DI的最佳方法吗?
Answers:
依赖注入是一个非常简单的概念的可怕名称(IMO)1。这是一个例子:
DbContext
)。此内部资源称为依赖项DbContext
从方法中删除了资源的创建和管理(即),并使调用者有责任提供此资源(作为方法参数或在实例化类时)
[1]:我来自较低层次的背景,花了几个月的时间坐下来学习依赖注入,因为它的名称暗示它会复杂得多,例如DLL Injection。是的Visual Studio(一般和我们的开发人员)是指.NET库(DLL,或者事实上组件),一个项目取决于作为依赖于所有没有帮助。甚至还有诸如Dependency Walker(depends.exe)之类的东西。
[编辑]我认为一些演示代码对某些人来说很方便,所以这里是一个(在C#中)。
没有依赖项注入:
public class Repository : IDisposable
{
protected DbContext Context { get; }
public Repository()
{
Context = new DbContext("name=MyEntities");
}
public void Dispose()
{
Context.Dispose();
}
}
然后,您的消费者将执行以下操作:
using ( var repository = new Repository() )
{
// work
}
使用依赖项注入模式实现的同一类如下所示:
public class RepositoryWithDI
{
protected DbContext Context { get; }
public RepositoryWithDI(DbContext context)
{
Context = context;
}
}
现在,调用者有责任实例化a DbContext
并将其传递(errm,inject)到您的类中:
using ( var context = new DbContext("name=MyEntities") )
{
var repository = new RepositoryWithDI(context);
// work
}
通常可以使用现实世界的类比更好地解释抽象概念。这是我的比喻:
您经营一家三明治店。您做出了惊人的三明治,但对面包本身一无所知。你只有白面包。您的工作完全集中在您用来将面包变成三明治的配料上。
但是,您的某些客户确实会更喜欢黑面包。有些人更喜欢全麦。您其实都不在乎这两种方式,只要它是类似大小的面包,就可以制作出任何令人赞叹的三明治。您还真的不想承担购买几种面包和增加库存的额外责任。即使您库存了几种类型的面包,也总会有一些您无法合理预见的带有某种异国风味面包的顾客。
因此,您制定了一条新规则:客户自备面包。您不再自己提供任何面包。这是一个双赢的局面:客户可以得到他们想要的确切的面包,而您不再需要费心购买不需要的面包。毕竟,您是三明治制造商,而不是面包师。
哦,为了容纳那些不想购买自己面包的顾客,您可以在隔壁开设第二家商店,出售原始的无味白面包。不自带面包的客户只需获得默认面包,然后找您来做三明治。
它并不完美,但是它突出了关键功能:将控制权交给消费者。内在的双赢是,您不再需要获取自己的依赖关系,并且消费者在选择依赖关系时不受阻碍。
为此,我们必须首先定义依赖关系和注入。
一个简单的例子就是将两个值相加的方法。显然,此方法需要将值相加。如果通过将它们作为参数传递来提供它们,则这已经是依赖注入的情况。另一种选择是将操作数实现为属性或全局变量。这样,就不会注入任何依赖关系,这些依赖关系可以在外部预先获得。
假设您改为使用属性,并分别命名为A和B。如果将名称更改为Op1和Op2,则会破坏Add方法。否则您的IDE会为您更新所有名称,关键是该方法也需要更新,因为它对外部资源具有依赖性。
此示例是基本示例,但您可以想象更复杂的示例,其中该方法对像图像这样的对象执行操作或从文件流中读取该对象。您是否想让方法到达图像,要求它知道它在哪里?否。您是否希望该方法打开文件本身,要求它知道在哪里寻找文件,甚至是从文件中读取文件?没有。
重点是:将一种方法的功能减少到其核心行为,并使该方法与环境分离。通过执行第二个操作可以得到第一个,您可以将其定义为依赖项注入。
好处:由于消除了对方法环境的依赖,因此对方法的更改不会影响环境,反之亦然。=>该应用程序变得易于维护(修改)。