为什么人使用依赖注入? [英] Why does one use dependency injection?

查看:141
本文介绍了为什么人使用依赖注入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了解依赖注射(DI),再次我失败了。这似乎只是愚蠢。我的code是从来没有一个烂摊子;我几乎不写虚函数和接口(虽然我在一个蓝色的月亮做一次),并使用json.net(有时也使用XML序列化),我的所有配置都奇迹般地序列化到一个类。

I'm trying to understand dependency injections (DI), and once again I failed. It just seems silly. My code is never a mess; I hardly write virtual functions and interfaces (although I do once in a blue moon) and all my configuration is magically serialized into a class using json.net (sometimes using an XML serializer).

我不明白它解决什么问题。它看起来像一个方式说:当你遇到这样的函数,返回一个对象,它是这种类型的,并使用这些参数/数据结果
可是......为什么我会永远使用?注意:我从来没有需要使用对象为好,但我明白这对

I don't quite understand what problem it solves. It looks like a way to say: "hi. When you run into this function, return an object that is of this type and uses these parameters/data."
But... why would I ever use that? Note I have never needed to use object as well, but I understand what that is for.

什么是要么建立一个网站或桌面应用程序,其中一个会用DI一些真正的情况呢?我能想出的情况下很容易的,为什么有人可能希望使用接口/虚拟功能于一身的游戏,但它是极其罕见的(足够罕见的,我不记得一个实例)使用,在非游戏类code

What are some real situations in either building a website or desktop application where one would use DI? I can come up with cases easily for why someone may want to use interfaces/virtual functions in a game, but it's extremely rare (rare enough that I can't remember a single instance) to use that in non-game code.

推荐答案

首先,我想说明一个假设,我作出这个答案。它并非总是如此,但往往

First, I want to explain an assumption that I make for this answer. It is not always true, but quite often:

接口是副词,​​类是名词。

Interfaces are adverbs, classes are nouns.

(事实上,有一些是名词以及接口,但我想在这里一概而论。)

(Actually, there are interfaces that are nouns as well, but I want to generalize here.)

所以,如接口可以为的IDisposable 的IEnumerable一些诸如 IPrintable 。一类是实际的实现一个或多个接口的:列表地图都可以是<$的实现C $ C>的IEnumerable 。

So, e.g. an interface may be something such as IDisposable, IEnumerable or IPrintable. A class is an actual implementation of one or more of these interfaces: List or Map may both be implementations of IEnumerable.

要抓住要点:通常你的类取决于对方。例如。你可以有一个数据库类访问数据库(哈,惊喜!;-)),但你也想这个课上做记录有关访问数据库。假设你有另一个类日志,那么数据库有一个依赖于日志

To get the point: Often your classes depend on each other. E.g. you could have a Database class which accesses your database (hah, surprise! ;-)), but you also want this class to do logging about accessing the database. Suppose you have another class Logger, then Database has a dependency to Logger.

到目前为止,一切都很好。

So far, so good.

您可以在此依赖模型的数据库里面与以下行类:

You can model this dependency inside your Database class with the following line:

var logger = new Logger();

和一切都很好。当你意识到你需要一群伐木工人是罚款的一天:有时要登录到控制台,有时到文件系统中,有时会使用TCP / IP和远程日志服务器,等等...

and everything is fine. It is fine up to the day when you realize that you need a bunch of loggers: Sometimes you want to log to the console, sometimes to the file system, sometimes using TCP/IP and a remote logging server, and so on ...

当然,你的的想改变所有code(同时你拥有它gazillions)和替换所有行

And of course you do NOT want to change all your code (meanwhile you have gazillions of it) and replace all lines

var logger = new Logger();

var logger = new TcpLogger();

首先,这是没有乐趣。第二,这是容易出错。第三,这是一个训练有素的猴子愚蠢的,重复性的工作。所以,你该怎么办?

First, this is no fun. Second, this is error-prone. Third, this is stupid, repetitive work for a trained monkey. So what do you do?

显然,这是引入一个接口一个不错的主意 ICanLog 由所有的各种记录器来实现(或类似)。因此,在您code第1步是你做的:

Obviously it's a quite good idea to introduce an interface ICanLog (or similar) that is implemented by all the various loggers. So step 1 in your code is that you do:

ICanLog logger = new Logger();

现在的类型推断不会改变任何类型多,你总是有一个单一的接口来进行开发。接下来的步骤是,你不希望有新记录器()一遍又一遍。所以你把可靠性来创建新实例来一个中央工厂类,你会得到code,如:

Now the type inference doesn't change type any more, you always have one single interface to develop against. The next step is that you do not want to have new Logger() over and over again. So you put the reliability to create new instances to a single, central factory class, and you get code such as:

ICanLog logger = LoggerFactory.Create();

该工厂本身决定什么样的记录器来创建。您code不关心任何更长的时间,如果你想改变记录器的类型时,你改变它的一次的:里面的工厂

现在,当然,你可以概括这家工厂,并使其成为任何类型的工作:

Now, of course, you can generalize this factory, and make it work for any type:

ICanLog logger = TypeFactory.Create<ICanLog>();

这某处需要TypeFactory当请求特定的接口类型的实例哪些实际的类,所以你需要一个映射配置数据。当然,你可以做你的code这里面映射,但随后型变化意味着重新编译。但你也可以把这个映射的XML文件,例如里面。这可以让你改变实际使用类即使编译时间后(!),动态意味着,而不需要重新编译!

Somewhere this TypeFactory needs configuration data which actual class to instantiate when a specific interface type is requested, so you need a mapping. Of course you can do this mapping inside your code, but then a type change means recompiling. But you could also put this mapping inside an XML file, e.g.. This allows you to change the actually used class even after compile time (!), that means dynamically, without recompiling!

要给你一个很好的例子为这样的:想想,这并不正常登录软件,但是当你的客户打电话要求帮助,因为他有一个问题,你送他的是一个更新的XML配置文件,现在他已经启用日志记录,并且您的支持可以使用日志文件来帮助你的客户。

To give you a useful example for this: Think of a software that does not log normally, but when your customer calls and asks for help because he has a problem, all you send to him is an updated XML config file, and now he has logging enabled, and your support can use the log files to help your customer.

而现在,当您更换名字一点点,你结束了一个简单的实现了的服务定位器的,这是两种模式一的控制反转的(因为你在反转谁决定什么确切类实例化控制)。

And now, when you replace names a little bit, you end up with a simple implementation of a Service Locator, which is one of two patterns for Inversion of Control (since you invert control over who decides what exact class to instantiate).

这所有的一切降低依赖于你的code,但现在所有的code具有依赖中央,单一的服务定位器。

All in all this reduces dependencies in your code, but now all your code has a dependency to the central, single service locator.

依赖注入的是现在该行的下一步:刚刚摆脱这种单一依赖于服务定位器:除了各个阶级,要求实现的服务定位为一个特定的接口,你 - 再次 - 恢复了谁什么实例化控

Dependency injection is now the next step in this line: Just get rid of this single dependency to the service locator: Instead of various classes asking the service locator for an implementation for a specific interface, you - once again - revert control over who instantiates what.

使用依赖注入,你的数据库类现在有一个需要键入 ICanLog 的参数的构造函数:

With dependency injection, your Database class now has a constructor that requires a parameter of type ICanLog:

public Database(ICanLog logger) { ... }

现在你的数据库总是有一个记录器使用,但它不知道任何更多的地方这样记录从何而来。

Now your database always has a logger to use, but it does not know any more where this logger comes from.

而这正是一个DI框架进场:你再次配置您的映射,然后问你的DI框架实例为你您的应用程序。由于应用类需要一个 ICanPersistData 实施数据库注入 - 但是,它必须首先创建一个配置为 ICanLog 那种记录器的一个实例。等等...

And this is where a DI framework comes into play: You configure your mappings once again, and then ask your DI framework to instantiate your application for you. As the Application class requires an ICanPersistData implementation, an instance of Database is injected - but for that it must first create an instance of the kind of logger which is configured for ICanLog. And so on ...

因此​​,削减长话短说:依赖注入是如何在你的code删除依赖两种方式之一。这是编译时配置更改后非常有用,它,它的单元测试一个伟大的事情(因为它使得它很容易注入存根和/或模拟考试)。

So, to cut a long story short: Dependency injection is one of two ways of how to remove dependencies in your code. It is very useful for configuration changes after compile-time, and it it a great thing for unit testing (as it makes it very easy to inject stubs and / or mocks).

在实践中,有些事情你不能做没有一个服务定位器(例如,如果你不提前你有多少实例需要一个特定的接口都知道:一个DI框架总是注入每个参数只有一个实例,但你可以调用一个服务定位器在循环中,当然),因此最常每个DI框架还提供了服务定位器。

In practice, there are things you can not do without a service locator (e.g., if you do not know in advance how many instances you do need of a specific interface: A DI framework always injects only one instance per parameter, but you can call a service locator inside a loop, of course), hence most often each DI framework also provides a service locator.

但基本上,仅此而已。

希望有所帮助。

PS:我这里介绍一个叫技术的构造函数注入的,还有的属性注射哪里都不构造函数的参数,但属性被用于定义和解决依赖的。物业注入看作一个可选的依赖,和构造器注入强制性依赖。但在这个讨论已经超出了这个问题的范围。

PS: What I described here is a technique called constructor injection, there is also property injection where not constructor parameters, but properties are being used for defining and resolving dependencies. Think of property injection as an optional dependency, and of constructor injection as mandatory dependencies. But discussion on this is beyond the scope of this question.

这篇关于为什么人使用依赖注入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆