Amu*_*umu 467 dependency-injection inversion-of-control
根据Martin Fowler撰写的论文,控制反转是程序控制流程被颠倒的原则:代替程序员控制程序流程,外部源(框架,服务,其他组件)控制它.这就像我们把东西塞进别的东西.他提到了一个关于EJB 2.0的例子:
例如,会话Bean接口定义ejbRemove,ejbPassivate(存储到辅助存储)和ejbActivate(从被动状态恢复).你无法控制何时调用这些方法,只是它们的作用.容器打电话给我们,我们不打电话给它.
这导致框架和库之间的区别:
控制反转是使框架与库不同的关键部分.库本质上是一组可以调用的函数,这些日子通常组织成类.每个调用都会执行一些操作并将控制权返回给客户端.
我认为,DI是IOC的观点,意味着对象的依赖性被颠倒了:相反,它控制着自己的依赖关系,生命周期......还有其他东西为你做.但是,正如你用手告诉我的DI,DI不一定是IOC.我们仍然可以有DI而没有IOC.
然而,在本文中(来自pococapsule,另一个IOC C/C++框架),它表明,由于IOC和DI,IOC容器和DI框架远远优于J2EE,因为J2EE将框架代码混合到组件中因此不能成为普通的旧Java/C++对象(POJO/POCO).
除依赖注入模式之外的控制容器的反转(存档链接)
附加阅读以了解旧的基于组件的开发框架的问题是什么,这导致了上面的第二篇论文:为什么以及什么是控制反转(存档链接)
我的问题:IOC和DI究竟是什么?我很迷惑.基于pococapsule,IOC比仅仅反转对象或程序员和框架之间的控制更重要.
Gar*_*all 596
IoC是一个通用术语,而不是让应用程序在框架中调用方法,框架调用应用程序提供的实现.
DI是IoC的一种形式,其中实现通过构造函数/设置器/服务查找传递到对象,对象将"依赖"以便正确行为.
例如,不使用DI的IoC将是模板模式,因为实现只能通过子类更改.
DI框架旨在利用DI,并可以定义接口(或Java中的注释),以便于传递实现.
IoC容器是DI框架,可以在编程语言之外工作.在某些情况下,您可以配置哪些实现用于较少侵入性的元数据文件(例如XML).对于一些你可以做IoC通常是不可能的,就像在切入点注入一个实现.
Tom*_*uλa 195
简而言之,IoC是一个更广泛的术语,包括但不限于DI
术语"控制反转"(IoC)最初意味着任何类型的编程风格,其中整体框架或运行时控制程序流
在DI有一个名字之前,人们开始将管理依赖关系的框架称为控制容器的反转,很快,IoC的意义逐渐转向特定意义:控制依赖关系的反转.
控制反转(IoC)意味着对象不会创建他们依赖的其他对象来完成工作.相反,他们从外部源获取所需的对象(例如,xml配置文件).
依赖注入(DI)意味着这是在没有对象干预的情况下完成的,通常由传递构造函数参数和设置属性的框架组件完成.
Lap*_*mir 44
DI是IoC的子集
Pre*_*raj 38
IoC(I nversion o f C ontrol ): - 它是一个通用术语,以多种方式实现(事件,代理等).
DI(D ependency I njection): - DI是IoC的子类型,通过构造函数注入,setter注入或接口注入实现.
但是,Spring仅支持以下两种类型:
NullPointerException: bean does not exist
.构造函数注入是注入依赖项的最佳实践.kn3*_*n3l 16
IOC(控制器的反转):控制容器以获取对象的实例称为控制反转.意思是代替使用new运算符创建对象,让容器为您执行此操作.
DI(依赖注入):向对象注入属性的方法称为依赖注入.
We have three types of Dependency injection
1) Constructor Injection
2) Setter/Getter Injection
3) Interface Injection
Run Code Online (Sandbox Code Playgroud)
Spring只支持Constructor Injection和Setter/Getter Injection.
Jer*_*yal 11
由于所有答案都强调理论,我想用一个例子来证明第一种方法:
假设我们正在构建一个应用程序,其中包含一个功能,可以在订单发货后发送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!");
}
}
Run Code Online (Sandbox Code Playgroud)
上面的实现没有错,但是几乎没有问题:
- )假设在开发环境中,你想保存发送到文本文件的短信而不是使用短信网关来实现这一点; 我们最终会用另一个实现来改变(SMSService)的具体实现,我们正在失去灵活性并被迫在这种情况下重写代码.
- )我们最终会混合类的职责,我们(UIHandler)永远不应该知道(SMSService)的具体实现,这应该在使用"Interfaces"的类之外完成.实现这一点后,它将使我们能够通过交换与实现相同接口的另一个模拟服务一起使用的(SMSService)来改变系统的行为,该服务将SMS保存到文本文件而不是发送到mobileNumber.
为了解决上述问题,我们使用将由我们的(SMSService)和新的(MockSMSService)实现的接口,基本上新的接口(ISMSService)将公开两种服务的相同行为,如下面的代码:
public interface ISMSService
{
void SendSMS(string phoneNumber, string body);
}
Run Code Online (Sandbox Code Playgroud)
然后我们将更改我们的(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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们将能够使用相同的界面创建具有完全不同实现的新模拟服务(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);
}
}
Run Code Online (Sandbox Code Playgroud)
此时,我们可以更改(UIHandler)中的代码,以便轻松使用服务的具体实现(MockSMSService),如下所示:
public class UIHandler
{
public void SendConfirmationMsg(string mobileNumber)
{
ISMSService _SMSService = new MockSMSService();
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
Run Code Online (Sandbox Code Playgroud)
我们已经实现了很多灵活性并在代码中实现了关注点分离,但我们仍然需要对代码库进行更改以在两个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!");
}
}
Run Code Online (Sandbox Code Playgroud)
现在,将与类(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();
}
}
Run Code Online (Sandbox Code Playgroud)
与其直接对比 DI 和 IoC,不如从头开始:每个重要的应用程序都依赖于其他代码段。
所以我正在编写一个类,MyClass
,我需要调用...的方法,YourService
我需要以某种方式获取YourService
. 最简单、最直接的方法是自己实例化它。
YourService service = new YourServiceImpl();
直接实例化是获取依赖项的传统(程序性)方式。但它有许多缺点,包括与MyClass
to 的紧密耦合YourServiceImpl
,使我的代码难以更改和难以测试。MyClass
不在乎 的实现是什么YourService
样子,所以MyClass
不想负责实例化它。
我宁愿反转从责任MyClass
的东西之外MyClass
。最简单的方法就是将实例化调用 ( new YourServiceImpl();
) 移到其他某个类中。我可以将这个其他类命名为 Locator、Factory 或任何其他名称;但重点是MyClass
不再负责YourServiceImpl
。我已经颠倒了这种依赖。伟大的。
问题是,MyClass
仍然负责调用 Locator/Factory/Whatever。由于我为反转依赖所做的一切都是插入一个中间人,现在我与中间人耦合(即使我没有与中间人给我的具体对象耦合)。
我真的不在乎我的依赖项来自哪里,所以我不想负责调用(s)来检索它们。反转依赖本身还不够。我想反转对整个过程的控制。
我需要的是一段完全独立的代码,MyClass
可以插入(称之为框架)。那么我剩下的唯一责任就是声明我对YourService
. 该框架可以负责确定在何处、何时以及如何获取实例,并且只提供MyClass
它所需要的。最好的部分是MyClass
不需要了解框架。框架可以控制这个依赖连接过程。现在我已经反转控制(在反转依赖项之上)。
有多种连接MyClass
到框架的方法。注入就是这样一种机制,我通过它简单地声明一个我希望框架提供的字段或参数,通常是在它实例化MyClass
.
我认为所有这些概念之间的关系层次比这个线程中的其他图表显示的要复杂一些;但基本思想是,它是一种等级关系。我认为这与野外的 DIP同步。
控制反转是一种设计范例,其目标是为应用程序的目标组件(完成工作的组件)提供更多控制。
依赖注入是一种模式,用于创建其他对象所依赖的对象实例,而无需在编译时知道哪个类将用于提供该功能。
有几种基本技术可以实现控制反转。这些都是:
小智 6
控制反转是软件架构的通用设计原则,有助于创建易于维护的可重用、模块化软件框架。
这是一种设计原则,其中控制流是从通用编写的库或可重用代码中“接收”的。
为了更好地理解它,让我们看看我们在早期编码时是如何编码的。在过程/传统语言中,业务逻辑通常控制应用程序的流程并“调用”通用或可重用的代码/函数。例如,在一个简单的控制台应用程序中,我的控制流由程序指令控制,其中可能包括对一些通用可重用函数的调用。
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
Run Code Online (Sandbox Code Playgroud)
相比之下,对于 IoC,框架是“调用”业务逻辑的可重用代码。
例如,在基于 Windows 的系统中,框架已经可用于创建 UI 元素,如按钮、菜单、窗口和对话框。当我编写应用程序的业务逻辑时,框架的事件将调用我的业务逻辑代码(当事件被触发时),而不是相反。
尽管框架的代码不知道我的业务逻辑,但它仍然知道如何调用我的代码。这是通过使用事件/委托、回调等来实现的。这里的流程控制是“反转”的。
因此,控制流不是依赖于静态绑定对象,而是依赖于整个对象图和不同对象之间的关系。
依赖注入是一种实现 IoC 原理来解决对象依赖关系的设计模式。
简而言之,当您尝试编写代码时,您将创建和使用不同的类。一个类(A类)可以使用其他类(B类和/或D类)。因此,B 类和 D 类是 A 类的依赖项。
一个简单的比喻就是一辆汽车。汽车可能依赖于其他类别,如发动机、轮胎等。
依赖注入建议不应使用依赖类(此处为 Car 类)创建其依赖项(Engine 类和 Tyre 类),而应使用依赖项的具体实例来注入类。
让我们通过一个更实际的例子来理解。假设您正在编写自己的文本编辑器。除此之外,您还可以拥有一个拼写检查器,为用户提供检查文本中拼写错误的工具。此类代码的简单实现可以是:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
乍一看,一切看起来都很美好。用户将写入一些文本。开发人员将捕获文本并调用 CheckSpellings 函数,并找到他将向用户显示的拼写错误列表。
一切似乎都很顺利,直到有一天,一位用户开始在编辑器中编写法语。
为了提供对更多语言的支持,我们需要更多的拼写检查器。可能是法语、德语、西班牙语等。
在这里,我们创建了一个紧密耦合的代码,其中“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.
}
}
Run Code Online (Sandbox Code Playgroud)
在我们的示例中,TextEditor 类应该接收 ISpellChecker 类型的具体实例。
现在,可以将依赖项注入到构造函数、公共属性或方法中。
让我们尝试使用构造函数 DI 来更改我们的类。更改后的 TextEditor 类将如下所示:
Class TextEditor
{
ISpellChecker objSpellChecker;
string Text;
public void TextEditor(ISpellChecker objSC)
{
objSpellChecker = objSC;
}
public ArrayList <typos> CheckSpellings()
{
return objSpellChecker.CheckSpelling();
}
}
Run Code Online (Sandbox Code Playgroud)
这样,调用代码在创建文本编辑器时可以将适当的拼写检查器类型注入到文本编辑器的实例中。
您可以在这里阅读完整的文章
但是弹簧文档说他们是一样的.
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-introduction
在第一行" IoC也称为依赖注入(DI) ".
小智 5
IoC - 控制反转是通用术语,与语言无关,它实际上不是创建对象,而是描述创建时尚对象的方式.
DI - 依赖注入是具体术语,我们通过使用不同的注入技术在运行时提供对象的依赖关系.Setter Injection,构造函数注入或接口注入.
IOC(控制反转):将控制权交给容器以获取对象的实例称为控制反转。这意味着不是您使用new operator创建对象,而是让容器为您完成。
DI(Dependency Injection):将所需的参数(属性)从 XML 传递到对象(在 POJO CLASS 中)称为依赖注入。
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();
}
Run Code Online (Sandbox Code Playgroud)
使用依赖注入,依赖注入器将负责对象的实例化
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();
}
Run Code Online (Sandbox Code Playgroud)
上面将控制权交给其他一些(例如容器)进行实例化和注入的过程可以称为控制反转,而IOC容器为我们注入依赖的过程可以称为依赖注入。
IOC是程序控制流程倒置的原理:程序不是由程序员控制程序的流程,而是通过减少程序员的开销来控制流程。程序用来注入依赖的过程称为DI
这两个概念一起工作,为我们提供了一种编写更灵活、可重用和封装的代码的方法,这使它们成为设计面向对象解决方案的重要概念。
也推荐阅读。
您也可以在此处查看我的类似答案之一
IOC-DIP-DI
1- IOC:描述某些软件架构设计的一个方面的抽象原理,其中系统的控制流程与过程编程相比是相反的。
2- DIP:是面向对象编程(OOP)原理(SOLID 的 D)。
3- DI:是一种软件设计模式,实现控制反转,并允许程序设计遵循依赖反转原则。
IOC 和 DIP 是两个不相交的集合,DIP 是 DI、服务定位器和其他一些模式的超集
归档时间: |
|
查看次数: |
258167 次 |
最近记录: |