在过去的几天里,我有一种感觉,依赖注入应该被称为“我无法下定决心” - 模式。我知道这可能听起来很愚蠢,但实际上这是为什么我应该使用依赖注入(DI)的原因。通常据说我应该使用DI来实现更高水平的松散耦合,并且我得到了那个部分。但实际上......一旦我的选择落在MS SQL或MySQL上,我多久更换一次数据库。很少这么做对了?
有没有人有一些非常令人信服的理由说明DI是要走的路?
答案 0 :(得分:46)
两个字,单元测试。
DI最令人信服的原因之一是允许更轻松的单元测试而无需访问数据库并担心设置“测试”数据。
答案 1 :(得分:10)
DI对于解耦系统非常有用。如果您正在使用它是为了将数据库实现与应用程序的其余部分分离,那么您的应用程序非常简单,或者您需要对问题域进行更多分析并发现问题域中的哪些组件是最容易改变的,以及系统中具有大量耦合的组件。
当您的目标是代码重用,多功能性和对问题域中的更改的稳健性时,DI非常有用。
与项目的相关程度取决于代码的预期使用寿命。根据您正在进行的工作类型,对于您正在编写的大多数代码,从一个项目到下一个项目的零重用实际上可能是完全可以接受的。
使用DI的一个示例是创建一个应用程序,该应用程序可以使用DI为客户端注入定制,为多个客户端部署,也可以将其描述为GOF策略模式。使用DI框架可以促进许多GOF模式。
DI与企业应用程序开发更相关,在这种应用程序开发中,您拥有大量代码,复杂的业务需求以及期望(或希望)系统将维护多年或数十年。
答案 2 :(得分:7)
即使您在开发阶段没有更改程序的结构,您也会发现需要从程序的不同部分访问多个子系统。使用DI,您的每个课程都需要提供服务,而您无需手动提供所有接线。
这确实有助于我专注于软件设计中的事物之间的互动,而不是“因为别人以后需要它而需要携带的东西”。
此外,它还节省了大量编写样板代码的工作。我需要一个单身人士吗?我只是将一个类配置为一个。我可以用这样的“单身人士”进行测试吗?是的,我仍然可以(因为我只是将它配置为仅存在一次,但测试可以实例化替代实现)。
但是,顺便说一下,在我使用DI之前,我并没有真正理解它的价值,但尝试它对我来说真是让人大开眼界:我的设计比以前更加面向对象。 顺便说一句,在目前的应用程序中,我不进行单元测试(糟糕,糟糕的我),但我仍然不能再使用DI了。它可以更轻松地移动并保持课程简洁。
答案 3 :(得分:5)
虽然我在数据库示例中与您达成一致意见,但我发现有助于使用DI的一件大事是帮助我测试我在数据库之上构建的层。
这是一个例子......
你有你的数据库。
您拥有访问数据库并返回对象的代码
您拥有业务域对象,这些对象可以使用上一个项目的对象并使用它们执行某些逻辑。
如果将数据访问权限与业务域逻辑合并,则域对象可能难以测试。 DI允许您将自己的数据访问对象注入到域中,这样您就不依赖于数据库进行测试或可能进行演示(运行一个演示,其中一些数据是从xml而不是数据库中提取的)。
抽象第三方组件和这样的框架也会对你有帮助。
除了测试示例之外,还有一些地方可以通过“按合同设计”方法使用DI。您可能会发现创建一个类型的处理引擎是合适的,它会调用您注入其中的对象的方法。虽然它可能不会真正“处理它”,但它会运行在您提供的每个对象中具有不同实现的方法。
我看到了一个这样的例子,其中每个业务域对象都有一个“保存”功能,该功能在注入处理器后被调用。处理器使用配置信息修改了组件,Save处理了对象的主要状态。从本质上讲,DI补充了符合接口的对象的多态方法实现。
答案 4 :(得分:4)
依赖注入使您能够隔离地测试特定的代码单元。
假设我有一个类Foo
,例如在其构造函数中获取类Bar
的实例。 Foo
上的其中一种方法可能会检查Bar
的属性值是否允许对Bar
进行其他处理。
public class Foo
{
private Bar _bar;
public Foo(Bar bar)
{
_bar = bar;
}
public bool IsPropertyOfBarValid()
{
return _bar.SomeProperty == PropertyEnum.ValidProperty;
}
}
现在让我们假设Bar
被实例化,并且它的属性被设置为来自其构造函数中的某些数据源的数据。我怎样才能测试IsPropertyOfBarValid()
的{{1}}方法(忽略这是一个非常简单的例子)?好吧,Foo
依赖于传递给构造函数的Foo
实例,而后者又依赖于属性设置为的数据源中的数据。我们想要做的是通过某种方式将Bar
与它所依赖的资源隔离开来,以便我们可以单独测试它
这就是Dependency Injection的用武之地。我们想要的是有一些伪造Foo
传递给Bar
的实例的方法,这样我们就可以控制在这个假{{1}上设置的属性并实现我们要做的事情,测试Foo
的实现是否符合我们的期望,即Bar
时返回true,任何其他值返回false。
假对象有两种类型,Mocks和Stubs。存根为被测应用程序提供输入,以便可以对其他内容执行测试。另一方面,模拟为测试提供输入以决定pass \ fail。
Martin Fowler has a great article on the difference between Mocks and Stubs
答案 5 :(得分:1)
除了松耦合之外,由于DI,可以更轻松地完成任何类型的测试。您可以将测试中的类的现有依赖项替换为mock,dummy或甚至其他版本。如果创建了一个直接实例化其依赖关系的类,那么如果需要,通常很难甚至不可能将它们“存根”。
答案 6 :(得分:1)
我认为当您有许多服务/组件时,DI值得使用,其实现必须在运行时根据外部配置进行选择。 (请注意,此类配置可以采用XML文件的形式,也可以采用代码注释和单独类的组合;选择更方便的内容。)
否则,我只会使用一个ServiceLocator,它比整个DI框架更“轻松”,更容易理解。
对于单元测试,我更喜欢使用可以按需模拟对象的模拟API,而不是要求它们从测试“注入”到测试单元中。对于Java,一个这样的库是我自己的JMockit。
答案 7 :(得分:0)
我今晚才明白。 对我来说,依赖注入是一种实例化对象的方法,这些对象需要很多参数才能在特定的上下文中工作。
什么时候应该使用依赖注射? 如果以静态方式实例化对象,则可以使用依赖注入。例如,如果您使用可以将对象转换为XML文件或JSON文件的类,并且您只需要XML文件。如果不使用依赖注入,则必须实例化对象并配置很多东西。
什么时候不应该使用depandancy注射? 如果一个对象与请求参数一起实例化(在提交表单之后),则不应使用depandancy注入,因为该对象不是以静态方式实例化的。