❝
可能你不了解 控制反转 和 依赖注入,但是你却一直在使用它
使用过Spring的老铁,应该都听说过 “控制反转” 和 “依赖注入” ,也就是常说的IOC 和 DI,这两个概念通常会一起出现,那么这两者是一个概念吗?又该如何作区分,接下来我们就来研究一下,他俩到底是什么东西。
在正式开始之前,我先提出几个问题,然后我们带着问题开始接下来的研究。
❝
1.控制反转中的”控制”,具体指什么?反转又是从哪里反转到了哪里?
❝
2.依赖注入中的依赖是什么?注入又是怎么实现的?
❝
3.依赖注入和控制反转有什么区别呢?
控制反转
控制反转的英文翻译是 Inversion Of Control,对这个陌生没关系,它的简写你一定很熟悉,那就是IOC,不过这个IOC和Spring中的Ioc容器还不太一样,后面我们详细说明。
简单来说:IOC是一种框架设计思想,而spring的Ioc容器是一个具体的技术实现。
为了更好的说明IOC的思想,我们先看一下下面的例子:
非控制反转
public class TestCaseOne {
public boolean testOne() {
return true;
}
}
public class TestCaseTwo {
public boolean testTwo() {
return true;
}
}
public class Main {
public static void main(String[] args) {
TestCaseOne one = new TestCaseOne();
TestCaseTwo tow = new TestCaseTwo();
if(one.testOne()) {
System.out.println("test successful.");
} else {
System.out.println("test failed.");
}
if(tow.testTwo()) {
System.out.println("test successful.");
} else {
System.out.println("test failed.");
}
}
}
控制反转
public abstract class TestCaseBase {
public abstract boolean test();
}
public class TestCaseOne extends TestCaseBase{
@Override
public boolean test() {
return true;
}
}
public class TestCaseTwo extends TestCaseBase{
@Override
public boolean test() {
return true;
}
}
public class Application {
private static List<TestCaseBase> caseContainer = new ArrayList<>();
public static void addTestCase(TestCaseBase testCase) {
caseContainer.add(testCase);
}
public static void runTestCase() {
for (TestCaseBase testCase : caseContainer) {
if(testCase.test()) {
System.out.println("test successful.");
} else {
System.out.println("test failed.");
}
}
}
}
public class Main {
public static void main(String[] args) {
Application.addTestCase(new TestCaseOne());
Application.addTestCase(new TestCaseTwo());
Application.runTestCase();
}
}
在上面两段代码中,对比两个Main类中的main方法,你能看出什么差异吗?
上面两段代码实现的功能是一样的,都是执行测试用例,不过两者实现的方式差异却很大。
在第一段代码中,每一个测试用例的被测试方法,在main方法中,从上到下逐个被调用,调用代码是程序员自己编写的,也就是整个测试用例的执行流程中的“控制流”
—— 调用者调用被调用的过程,完全是程序员自己控制的。
而在第二段代码中,程序员需要做的事情主要是将测试用例添加到测试框架中,然后运行测试框架,具体测试用例中的测试方法以什么样的方式运行,以及在什么时间点被调用,程序员是不知道的, 也就是测试用例执行流程中的"控制流",不是程序员控制的,而是框架控制的
。
到这里,我们就可以回答上面关于控制反转的两个问题了。
“控制” 是指对程序执行流程的控制权,也就是调用者,在什么时候以什么方式调用被调用者的流程。
控制反转是指将流程的控制权从程序员手中转移到了框架里。
控制反转有什么好处呢?
使用控制反转的思想,可以让程序员更加专注编写业务代码,无需关注非业务代码运行流程的细节,可以提高工作效率,这也是oop中封装原则的初衷。
控制反转的思想在绝大部分的框架中都有应用。我们在使用第三方框架时,只需要根据框架的使用说明,将业务相关的代码”关联”(类似于依赖注入代码中的addTestCase)到框架中即可,而无需关心我们的业务代码,在框架中是何时被调用的。
有些框架将IOC的思想应用的非常好,将具体的”控制流”封装的很深,只阅读框架的源码很难理清楚整个调用流程,还需要结合ide的调试工具的辅助。阅读过框架源码的小伙伴一定深有体会吧。
总结来说,控制反转是一种设计思想,用来指导应用框架的设计,是“道”层面的内容。
依赖注入
依赖注入也是我们经常听到一个概念,对于不了解它的读者来说,感觉很高大上,如果你了解它后,就会觉得 “就这?”,不信接着往下看。
依赖注入简单来说就是:不通过new()方法在类内部创建被依赖的对象,而是将被依赖的对象,在外部创建好,通过依赖对象的构造方法或者其他参数设置方法,将被依赖的类给传递进来。
所以开头关于依赖注入的问题,现在也可以回答了:
依赖:就是 被new出来的对象,或者被需要的组件。
注入:就是一个将被依赖的组件传递到被依赖的组件中的过程。
为了更好的说明依赖注入,我们还是写一段代码来说明一下。
非依赖注入代码:
public abstract class Vehicle {
public abstract void drive();
}
public class Car extends Vehicle {
@Override
public void drive() {
System.out.println("drive car.");
}
}
public class GoHome {
private Vehicle vehicle ;
public GoHome() {
vehicle = new Car();
}
public void go() {
vehicle.drive();
}
}
依赖注入代码:
public class GoHomeWithDi {
private Vehicle vehicle ;
public GoHomeWithDi(Vehicle vehicle) {
this.vehicle = vehicle;
}
public void go() {
vehicle.drive();
}
}
在上面代码中,依赖对象是GoHome和GoHomeWithDi,被依赖对象是 vehicle ,在非依赖注入代码中,vehicle是代码中写死的,那么上面代码表达的语义就是 **”只能开车回家”**。
而依赖注入的实现方式,是通过依赖对象的构造方法将被依赖对象注入进来。表达的语义是 “想怎么回家就怎么回家”,因为回家的乘坐的交通工具没有写死,可以选择的方式很多。
相比控制反转,依赖注入是一种具体的编程技巧,是 “术” 层面的内容。
到这里我想你应该可以回答开头的第三个问题了,控制反转和依赖注入,不是一个层面的内容。
在实际的工作中,一个项目里可能会有成百上千的类,类之间存在比较复杂的依赖关系,如果这些依赖关系,使用上面依赖注入的实现方式的话,出现错误的可能性会比较大,而且维护成本也比较高,再加上依赖注入和具体业务关系也不大,依赖注入的过程完全可以结合控制反转的思想 “反转” 给框架来实现。
而Spring的Ioc容器就是一款比较优秀的依赖注入框架。既然如此,为什么spring还自称是IOC容器(控制反转容器)呢?
其实spring的容器,被称为控制反转容器,还是依赖注入框架都没有错,使用控制反转容器,是描述这个框架的设计思想,将依赖注入的流程反转给了框架,而称之为依赖注入框架,是因为spring的这个容器就是为了解决这个依赖注入的问题,用依赖注入框架来描述更具体,也更具有针对性。
❝
今日分享如果对你有帮助,帮忙点个在看
原文始发于微信公众号(小李哥编程):终于有人把控制反转和依赖注入讲清楚了
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/268029.html