根据paper written by Martin Fowler,控制反转是程序控制流被反转的原则:代替程序员控制程序的流程,外部源(框架,服务,其他组件)控制它。这就像我们把东西塞进别的东西。他提到了一个关于EJB 2.0的例子:
例如Session Bean接口 定义ejbRemove,ejbPassivate (存储到二级存储),和 ejbActivate(从被动恢复) 州)。你什么时候无法控制 这些方法被称为,只是什么 他们是这样。容器叫我们,我们 不要叫它。
这导致了框架和库之间的区别:
控制倒置是其中的关键部分 什么使框架与a不同 图书馆。图书馆本质上是一个 你可以调用的一组函数, 这些天通常组织成 类。每次通话都会做一些工作 将控制权交还给客户。
我认为,DI是IOC的观点,意味着对象的依赖性被颠倒了:相反,它控制着自己的依赖关系,生命周期......还有其他东西为你做。但是,正如你用手告诉我的DI,DI不一定是IOC。我们仍然可以有DI而且没有IOC。
然而,在本文中(来自pococapsule,另一个IOC C / C ++框架),它表明,由于IOC和DI,IOC容器和DI框架远远优于J2EE,因为J2EE混合了框架代码进入组件,因此不能成为普通的旧Java / C ++对象(POJO / POCO)。
Inversion of Control Containers other than the Dependency Injection pattern(存档链接)
了解旧的基于组件的开发框架的问题的附加阅读,这导致上面的第二篇论文:Why and what of Inversion of Control(存档链接)
我的问题:IOC和DI究竟是什么?我很迷惑。基于pococapsule,IOC比仅仅反转对象或程序员与框架之间的控制更为重要。
答案 0 :(得分:544)
IoC 是一个通用术语,而不是让应用程序在框架中调用方法,框架调用应用程序提供的实现。
DI 是IoC的一种形式,其中实现通过构造函数/设置器/服务查找传递到对象中,对象将“依赖”以便正常运行。
不使用DI的IoC ,例如是模板模式,因为实现只能通过子类更改。
DI框架旨在利用DI并可以定义接口(或Java中的注释),以便于传递实现。
IoC容器是DI框架,可以在编程语言之外工作。在某些情况下,您可以配置哪些实现用于较少侵入性的元数据文件(例如XML)。对于一些你可以做IoC,这通常是不可能的,比如在pointcuts注入一个实现。
答案 1 :(得分:190)
简而言之,IoC是一个更广泛的术语,包括但不限于DI
术语“控制反转”(IoC)最初意味着任何类型的编程风格 框架或运行时控制程序流程
在DI有一个名字之前,人们开始引用管理依赖关系作为反转的框架 控制容器,很快,IoC的意义逐渐向这个特定含义漂移:控制依赖性的反转。
控制反转(IoC)意味着对象不会创建他们依赖的其他对象来完成工作。相反,他们从外部源获取所需的对象(例如,xml配置文件)。
依赖注入(DI)意味着没有对象干预,通常是通过构造函数参数和设置属性的框架组件。
答案 2 :(得分:43)
DI是IoC的子集
答案 3 :(得分:28)
IoC (我 nversion o f C ontrol): - 这是一个通用术语,并在几个实现方式(事件,代表等)。
DI ( D 依赖 I njection): - DI是IoC的子类型,由构造函数注入实现,setter注入或接口注入。
但是,Spring仅支持以下两种类型:
NullPointerException: bean does not exist
。构造函数注入是注入依赖项的最佳实践。答案 4 :(得分:14)
IOC(控制器反转):控制容器以获取对象的实例称为控制反转。意思是代替使用new运算符创建对象,让容器为你做的。
DI(依赖注入):向对象注入属性的方法称为依赖注入。
We have three types of Dependency injection
1) Constructor Injection
2) Setter/Getter Injection
3) Interface Injection
Spring只支持Constructor Injection和Setter / Getter Injection。
答案 5 :(得分:7)
由于所有答案都强调理论,我想用一个例子来证明第一种方法:
假设我们正在构建一个应用程序,其中包含一个功能,可以在订单发货后发送SMS确认消息。 我们将有两个类,一个负责发送SMS(SMSService),另一个负责捕获用户输入(UIHandler),我们的代码如下所示:
public class SMSService
{
public void SendSMS(string mobileNumber, string body)
{
SendSMSUsingGateway(mobileNumber, body);
}
private void SendSMSUsingGateway(string mobileNumber, string body)
{
/*implementation for sending SMS using gateway*/
}
}
public class UIHandler
{
public void SendConfirmationMsg(string mobileNumber)
{
SMSService _SMSService = new SMSService();
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
以上实施没有错,但问题很少:
- )假设在开发环境中,你想保存发送到文本文件而不是使用SMS网关的短信来实现这一点;我们最终会改变(SMSService)的具体实现与另一个实现,我们正在失去灵活性,并在这种情况下被迫重写代码。
- )我们最终会混合类的职责,我们(UIHandler)永远不应该知道(SMSService)的具体实现,这应该在使用“Interfaces”的类之外完成。实现这一点后,它将使我们能够通过交换与实现相同接口的另一个模拟服务一起使用的(SMSService)来改变系统的行为,该服务将SMS保存到文本文件而不是发送到mobileNumber。
为了解决上述问题,我们使用将由我们的(SMSService)和新的(MockSMSService)实现的接口,基本上新接口(ISMSService)将公开与下面代码相同的两种服务行为:
public interface ISMSService
{
void SendSMS(string phoneNumber, string body);
}
然后我们将更改我们的(SMSService)实现以实现(ISMSService)接口:
public class SMSService : ISMSService
{
public void SendSMS(string mobileNumber, string body)
{
SendSMSUsingGateway(mobileNumber, body);
}
private void SendSMSUsingGateway(string mobileNumber, string body)
{
/*implementation for sending SMS using gateway*/
Console.WriteLine("Sending SMS using gateway to mobile:
{0}. SMS body: {1}", mobileNumber, body);
}
}
现在我们将能够使用相同的界面创建具有完全不同实现的新模拟服务(MockSMSService):
public class MockSMSService :ISMSService
{
public void SendSMS(string phoneNumber, string body)
{
SaveSMSToFile(phoneNumber,body);
}
private void SaveSMSToFile(string mobileNumber, string body)
{
/*implementation for saving SMS to a file*/
Console.WriteLine("Mocking SMS using file to mobile:
{0}. SMS body: {1}", mobileNumber, body);
}
}
此时,我们可以更改(UIHandler)中的代码,以便轻松使用服务的具体实现(MockSMSService),如下所示:
public class UIHandler
{
public void SendConfirmationMsg(string mobileNumber)
{
ISMSService _SMSService = new MockSMSService();
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
我们在代码中实现了很多灵活性并实现了关注点的分离,但我们还需要对代码库进行更改以在两个SMS服务之间切换。所以我们需要实现 依赖注入 。
为了实现这一点,我们需要对我们的(UIHandler)类构造函数实现更改以通过它传递依赖关系,通过这样做,使用(UIHandler)的代码可以确定要使用的(ISMSService)的具体实现:
public class UIHandler
{
private readonly ISMSService _SMSService;
public UIHandler(ISMSService SMSService)
{
_SMSService = SMSService;
}
public void SendConfirmationMsg(string mobileNumber)
{
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
现在,将与类(UIHandler)通信的UI表单负责传递要使用的接口(ISMSService)的实现。这意味着我们已经反转了控件,(UIHandler)不再负责决定使用哪个实现,调用代码就是这样。我们实施了 控制反转 原则,其中DI是其中的一种。
UI表单代码如下:
class Program
{
static void Main(string[] args)
{
ISMSService _SMSService = new MockSMSService(); // dependency
UIHandler _UIHandler = new UIHandler(_SMSService);
_UIHandler.SendConfirmationMsg("96279544480");
Console.ReadLine();
}
}
答案 6 :(得分:5)
但是弹簧文档说它们是相同的。
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-introduction
在第一行“ IoC也称为依赖注入(DI)”。
答案 7 :(得分:5)
与其直接对比 DI 和 IoC,不如从头开始:每个重要的应用程序都依赖于其他代码段。
所以我正在编写一个类 MyClass
,我需要调用 YourService
的方法......不知何故我需要获取 YourService
的实例。最简单、最直接的方法就是自己实例化。
YourService service = new YourServiceImpl();
直接实例化是获取依赖项的传统(程序性)方式。但它有许多缺点,包括 MyClass
与 YourServiceImpl
的紧密耦合,使我的代码难以更改和难以测试。 MyClass
不关心 YourService
的实现是什么样子,所以 MyClass
不想负责实例化它。
我更愿意将责任从MyClass
倒置到MyClass
之外的事情。最简单的方法就是将实例化调用 (new YourServiceImpl();
) 移动到其他某个类中。我可以将这个其他类命名为 Locator、Factory 或任何其他名称;但重点是 MyClass
不再负责 YourServiceImpl
。我已经颠倒了这种依赖。太好了。
问题是,MyClass
仍然负责调用 Locator/Factory/Whatever。由于我为反转依赖所做的一切只是插入一个中间人,现在我与中间人耦合了(即使我没有与中间人给我的具体对象耦合)。
我真的不在乎我的依赖项来自哪里,所以我不想负责进行调用来检索它们。反转依赖本身还不够。我想反转整个过程的控制。
我需要的是一段完全独立的代码,MyClass
插入其中(称之为框架)。那么我剩下的唯一责任就是声明我对 YourService
的依赖。该框架可以负责确定在何处、何时以及如何获取实例,并只提供 MyClass
所需的内容。最好的部分是 MyClass
不需要了解框架。框架可以控制这个依赖连接过程。现在我已经反转控制(在反转依赖项之上)。
有多种方法可以将 MyClass
连接到框架中。注入就是这样一种机制,我可以通过它简单地声明一个我希望框架提供的字段或参数,通常是在它实例化 MyClass
时。
我认为所有这些概念之间的关系层次比该线程中其他图表显示的要复杂一些;但基本思想是,它是一种等级关系。我认为这与 DIP in the wild 同步。
答案 8 :(得分:5)
IoC - 控制反转是通用术语,与语言无关,它实际上不是创建对象,而是描述正在创建时尚对象。
DI - 依赖注入是具体的术语,我们通过使用不同的注入技术在运行时提供对象的依赖关系。 Setter Injection,构造函数注入或接口注入。
答案 9 :(得分:4)
控制反转是一种设计范例,其目标是为应用程序的目标组件提供更多控制,即完成工作的组件。
依赖注入是一种模式,用于创建其他对象所依赖的对象实例,而无需在编译时知道将使用哪个类来提供该功能。
实现控制反转有几种基本技术。这些是:
答案 10 :(得分:2)
DI 和 IOC 是两个设计模式,主要侧重于提供组件之间的松散耦合,或者只是我们解除对象之间的传统依赖关系以使对象彼此不紧密的方式。
通过以下示例,我试图解释这两个概念。
以前我们正在编写像这样的代码
Public MyClass{
DependentClass dependentObject
/*
At somewhere in our code we need to instantiate
the object with new operator inorder to use it or perform some method.
*/
dependentObject= new DependentClass();
dependentObject.someMethod();
}
使用依赖注入,依赖注入器将负责对象的实例化
Public MyClass{
/* Dependency injector will instantiate object*/
DependentClass dependentObject
/*
At somewhere in our code we perform some method.
The process of instantiation will be handled by the dependency injector
*/
dependentObject.someMethod();
}
将控件提供给实例化和注入的其他一些(例如容器)的上述过程可以称为控制反转,IOC容器为我们注入依赖关系的过程可以称为依赖注入
IOC是程序控制流被反转的原则:程序控制程序流程,而不是程序员控制程序流程,程序通过减少程序员的开销来控制流程。 >程序用于注入依赖关系的过程称为DI
这两个概念共同为我们提供了一种编写更灵活,可重用和封装代码的方法,这使它们成为设计面向对象解决方案时的重要概念。
还建议阅读。
你也可以在这里查看我的一个类似答案
Difference between Inversion of Control & Dependency Injection
答案 11 :(得分:2)
控制反转: - 这是一个通用术语,用于解耦依赖关系并委托他们的配置,这可以通过多种方式实现(事件,代理等)。
依赖注入: - DI是IOC的子类型,通过构造函数注入,setter注入或方法注入来实现。
以下文章非常巧妙地描述了这一点。
https://www.codeproject.com/Articles/592372/Dependency-Injection-DI-vs-Inversion-of-Control-IO
答案 12 :(得分:2)
IOC 表示管理应用程序类的外部类和外部类表示容器管理应用程序类之间的依赖关系。 IOC 的基本概念是程序员不需要创建对象,而是描述如何创建对象。
IoC容器执行的主要任务是: 实例化应用程序类。配置对象。组装对象之间的依赖关系。
DI 是使用setter注入或构造函数注入在运行时提供对象依赖项的过程。
答案 13 :(得分:2)
控制反转是软件架构的通用设计原则,有助于创建易于维护的可重用模块化软件框架。
这是一种设计原则,其中从通用编写的库或可重用代码“接收”控制流。
为了更好地理解它,让我们看看我们在编码的早期代码中是如何编码的。在过程/传统语言中,业务逻辑通常控制应用程序的流程并“调用”通用或可重用的代码/功能。例如,在一个简单的控制台应用程序中,我的控制流程由我的程序指令控制,可能包括对一些通用可重用函数的调用。
print ("Please enter your name:");
scan (&name);
print ("Please enter your DOB:");
scan (&dob);
//More print and scan statements
<Do Something Interesting>
//Call a Library function to find the age (common code)
print Age
在对比中,对于IoC,框架是“调用”业务逻辑的可重用代码。
例如,在基于Windows的系统中,框架已经可用于创建UI元素,如按钮,菜单,窗口和对话框。当我编写应用程序的业务逻辑时,框架的事件将调用我的业务逻辑代码(当事件被触发时),而不是相反。
虽然框架的代码不了解我的业务逻辑,但它仍然知道如何调用我的代码。这是通过使用事件/代理,回调等来实现的。这里的流控制是“反转的”。
因此,流不依赖于静态绑定对象的控制流,而是取决于整体对象图和不同对象之间的关系。
依赖注入是一种设计模式,它实现了IoC原则,用于解决对象的依赖关系。
简单来说,当您尝试编写代码时,您将创建并使用不同的类。一类(A类)可以使用其他类(B类和/或D类)。因此,B类和D类是A类的依赖。
一个简单的比喻将是一类汽车。汽车可能依赖于其他类别,如发动机,轮胎等。
依赖注入表明,不是依赖类(Class Car)创建它的依赖项(Class Engine和类Tire),而是应该使用依赖项的具体实例注入类。
让我们用更实际的例子来理解。请考虑您正在编写自己的TextEditor。除此之外,您还可以使用拼写检查程序为用户提供检查其文本中拼写错误的工具。这种代码的简单实现可以是:
Class TextEditor
{
//Lot of rocket science to create the Editor goes here
EnglishSpellChecker objSpellCheck;
String text;
public void TextEditor()
{
objSpellCheck = new EnglishSpellChecker();
}
public ArrayList <typos> CheckSpellings()
{
//return Typos;
}
}
乍一看,一切看起来都很美好。用户将编写一些文本。开发人员将捕获文本并调用CheckSpellings函数,并找到他将向用户显示的错别字列表。
在一个用户开始在编辑器中编写法语的好日子之前,一切似乎都很有效。
为了提供更多语言的支持,我们需要更多的SpellCheckers。可能是法语,德语,西班牙语等。
在这里,我们创建了一个紧密耦合的代码,其中“English”SpellChecker与我们的TextEditor类紧密耦合,这意味着我们的TextEditor类依赖于EnglishSpellChecker,或者换句话说,EnglishSpellCheker是TextEditor的依赖。我们需要删除这种依赖。此外,我们的文本编辑器需要一种方法来根据开发人员在运行时的自由裁量权来保存任何拼写检查器的具体参考。
因此,正如我们在引入DI时所看到的那样,它表明应该为类注入其依赖项。因此,调用代码应该将所有依赖项注入被调用的类/代码。所以我们可以将代码重构为
interface ISpellChecker
{
Arraylist<typos> CheckSpelling(string Text);
}
Class EnglishSpellChecker : ISpellChecker
{
public override Arraylist<typos> CheckSpelling(string Text)
{
//All Magic goes here.
}
}
Class FrenchSpellChecker : ISpellChecker
{
public override Arraylist<typos> CheckSpelling(string Text)
{
//All Magic goes here.
}
}
在我们的示例中,TextEditor类应该接收ISpellChecker类型的具体实例。
现在,可以在Constructor,Public Property或方法中注入依赖项。
让我们尝试使用Constructor DI更改我们的类。更改后的TextEditor类看起来像:
Class TextEditor
{
ISpellChecker objSpellChecker;
string Text;
public void TextEditor(ISpellChecker objSC)
{
objSpellChecker = objSC;
}
public ArrayList <typos> CheckSpellings()
{
return objSpellChecker.CheckSpelling();
}
}
这样调用代码在创建文本编辑器时可以将相应的SpellChecker类型注入到TextEditor的实例中。
您可以阅读完整的文章here
答案 14 :(得分:1)
IOC(控制反转):控制容器以获取对象的实例称为控制反转。这意味着您不是使用 new operator 创建对象,而是让容器为您执行此操作。
DI(依赖注入):将所需的参数(属性)从XML传递到对象(在POJO CLASS中)称为依赖注入。
答案 15 :(得分:1)
我认为,无需陷入面向对象的杂草,就可以清楚地演示这个想法,这似乎使这个想法变得混乱。
// dependency injection
function doSomething(dependency) {
// do something with your dependency
}
// in contrast to creating your dependencies yourself
function doSomething() {
dependency = getDependencySomehow()
}
// inversion of control
application = makeApp(authenticate, handleRequest, sendResponse)
application.run(getRequest())
// in contrast to direct control or a "library" style
application = makeApp()
request = application.getRequest()
if (application.authenticate(request.creds)) {
response = application.handleRequest(request)
application.sendResponse(response)
}
如果倾斜头部并斜视眼睛,您会发现DI是IoC的一种特殊实现方式,具有特殊的问题。您不是将模型和行为注入到应用程序框架或高阶操作中,而是将变量注入到函数或对象中。
答案 16 :(得分:0)
DIP vs DI vs IoC
[Dependency Inversion Principle(DIP)] 是 SOLID
[About] 的一部分,它要求您使用抽象而不是实现
依赖注入(DI) - 使用聚合而不是组合[About] 在这种情况下,外部对象负责内部逻辑。这让你有更多动态和可测试的方法
class A {
B b
//injecting B via constructor
init(b: B) {
self.b = b
}
}
控制反转 (IoC) 非常高级的定义,更多的是关于控制流。最好的例子是Inversion of Control(IoC) Container or Framework
[About]。例如,GUI 是您没有控件的框架,您可以做的一切只是实现框架的接口,该接口将在框架中发生某些操作时被调用。因此控制权从您的应用程序转移到正在使用的框架
DIP + DI
class A {
IB ib
init(ib: IB) {
self.ib = ib
}
}
你也可以使用:
更复杂的例子
依赖规则多层/模块结构
伪代码:
interface InterfaceInputPort {
func input()
}
interface InterfaceOutputPort {
func output()
}
class A: InterfaceOutputPort {
let inputPort = B(outputPort: self)
func output() {
print("output")
}
}
class B: InterfaceInputPort {
let outputPort: InterfaceOutputPort
init(outputPort: InterfaceOutputPort) {
self.outputPort = outputPort
}
func input() {
print("input")
}
}
答案 17 :(得分:0)
IOC-DIP-DI
1- IOC :一种抽象原理,描述了某些软件体系结构设计的一个方面,与过程编程相比,该系统的控制流是相反的。
2- DIP :是面向对象编程(OOP)原理(SOLID的D)。
3- DI :是一种软件设计模式,可实现控制反转,并允许程序设计遵循依赖关系反转原理。
IOC & DIP are two disjoint sets and DIP is the super set of DI, service locator and some other patterns
答案 18 :(得分:0)
IoC概念最初是在过程编程时代听到的。因此,IoC从历史的角度讨论了控制权-流的所有权的倒置,即谁有责任按期望的顺序调用功能-无论是功能本身还是功能。您应该将其转换为某个外部实体吗?
然而,一旦OOP出现,人们就开始在OOP上下文中谈论IoC,在这种情况下,应用程序除了控制流之外还关注对象的创建及其关系。这样的应用程序想要反转对象创建的所有权(而不是控制流),并且需要一个容器来负责对象创建,对象生命周期和注入应用程序对象的依赖关系,从而消除了应用程序其他创建具体对象的对象。
从这个意义上讲,DI与Io C 不同,因为它与控制流无关,但是它是 一种Io * >,即对象创建所有权的倒置。
答案 19 :(得分:0)
对于这个问题,我想说Wiki已经提供了详细且易于理解的解释。我将在此引用最重要的内容。
在面向对象的编程中,有几种基本技术可以 实现控制反转。这些是:
- 使用服务定位器模式 示例构造函数注入参数注入setter注入 接口注入;
- 使用上下文查询;
- 使用模板方法设计模式;
- 使用策略设计模式
依赖注入是一种技术,其中一个对象(或静态) 方法)提供另一个对象的依赖关系。依赖是 可以使用的对象(服务)。注射是通过 对将使用它的依赖对象(客户端)的依赖。
答案 20 :(得分:0)
1)DI是Child-&gt; obj取决于parent-obj。动词取决于重要性。 2)IOC是Child-&gt; obj在平台下执行。平台可以是学校,大学,舞蹈班。此处执行是在任何平台提供商下具有不同含义的活动。
实际例子: `
//DI
child.getSchool();
//IOC
child.perform()// is a stub implemented by dance-school
child.flourish()// is a stub implemented by dance-school/school/
`
-AB
答案 21 :(得分:0)
我在Dzone.com找到了最好的例子,这对于理解IOC和DI之间的真正差异非常有用
“IoC就是当你让其他人为你创建对象时。”因此,代码不是在你的代码中编写“new”关键字(例如,MyCode c = new MyCode()),而是由其他人创建。这个“别人”通常被称为IoC容器。这意味着我们将rrsponsibility(控制)移交给容器以获取对象的实例称为Inversion of Control。, 意味着你不是使用new运算符创建对象,而是让容器为你做。
DI(Dependency Injection): Way of injecting properties to an object is
called
Dependency injection.
We have three types of Dependency injection
1) Constructor Injection
2) Setter/Getter Injection
3) Interface Injection
Spring will support only Constructor Injection and Setter/Getter Injection.
答案 22 :(得分:0)
// ICO,DI,10年前,这是他们的方式:
public class AuditDAOImpl implements Audit{
//dependency
AuditDAO auditDAO = null;
//Control of the AuditDAO is with AuditDAOImpl because its creating the object
public AuditDAOImpl () {
this.auditDAO = new AuditDAO ();
}
}
现在使用Spring 3,4或最新版本如下
public class AuditDAOImpl implements Audit{
//dependency
//Now control is shifted to Spring. Container find the object and provide it.
@Autowired
AuditDAO auditDAO = null;
}
总体而言,控件从耦合代码的旧概念反转到Spring等框架,使对象可用。据我所知,当我们使用Constructor或setter将依赖对象注入另一个对象时,我知道IOC和依赖注入。注入基本上意味着将其作为参数传递。在春天,我们有XML&amp;基于注释的配置,我们定义bean对象并使用Constructor或setter注入样式传递依赖对象。
答案 23 :(得分:0)
让我们从SOLID的D开始,看看Scott Millett的书“Professional ASP.NET Design Patterns”中的DI和IoC:
依赖性倒置原则(DIP)
DIP 就是将您的类与具体内容隔离开来 实现并让它们依赖于抽象类或 接口。它将编码的咒语提升为界面 而不是实现,这增加了系统内的灵活性 确保您没有与一个实现紧密结合。
依赖注入(DI)和控制反转(IoC)
与DIP紧密相关的是DI原则和IoC原则。 DI 是通过提供低级别或依赖级别的行为 构造函数,方法或属性。与DI一起使用,这些 依赖类可以反转为接口或抽象类 这将导致松散耦合的系统具有高度可测试性 容易改变。
在 IoC 中,系统的控制流程被反转 与程序编程相比。一个例子是 IoC 容器,其目的是将服务注入客户端代码 没有客户端代码指定具体实现。 在这种情况下被反转的控制是行为 客户获得服务。
Millett,C(2010)。专业的ASP.NET设计模式。威利出版社。 7-8。