一个快要被问烂了的面试题:谈谈你对IoC的理解
其实究其原因是国内的大多数教程或者技术分享在解释IoC时都会直接绑定IoC容器去解释,这会让很多通过Spring接触IoC的同学误解为只有Spring才IoC,或者只有用了IoC容器才叫IoC,这种理解实际上是非常狭隘的,结果就是知其然不知其所以然,体会不到IoC的思想和精髓。
实际上IoC是一种设计原则,早在1988年就被提出,而Spring是在很多年后才发布。
也就是说,IoC是为了落地六大设计原则之依赖倒置原则。
所谓依赖倒置原则大体上可以总结为:
A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。
B.抽象不应该依赖于具体,具体应该依赖于抽象。
一句话的目的是为了解耦。
举个栗子
实现一个老王开奥迪的业务,那么我们需要两个类:司机类和奥迪类
public class Driver{
private Aodi audi=new Aodi();
public void drive(){
audi.run();
}
}
public class Aodi{
public void run(){
System.out.println("爷是奥迪")
}
}
可以理解,司机类在业务中是位于奥迪类上层的。司机类需要实例化一个 奥迪对象,调用奥迪的run方法,实际上在new奥迪对象的一瞬间,司机类与奥迪类已经耦合了,从此没有奥迪,这司机也没法活。
让我们测试一下
public class Test{
public static void main(String[] args){
Driver laowang=new Driver();
laowang.drive();
}
}
此时老王当炒币狗发达了,要上奔驰了,咋整?于是乎我们只能在写一个奔驰类,然后修改司机类,把原来New的奥迪改成奔驰。
public class Driver{
//private Aodi audi=new Aodi();
private Benz benz=new Benz();
public void drive(){
//audi.run();
benz.run();
}
}
那如果过两天老王老婆跟家里的厨子卷走家产开着奔驰只留下一辆自行车跑了呢?
这种因为下层(交通工具)变动而导致需要上层(司机)频繁修改的设计是与依赖倒置原则相悖的,依赖倒置原则教我们,高层应该依赖于抽象。
所以我们抽象出一个交通工具接口,所有的交通工具均实现这个接口(有点像策略模式)
public interface Car(){
public void run();
}
public class Benz implements Car(){
@Override
public void run(){
System.out.println("爷是奥迪");
}
}
这样,我们的司机类也不必再只依赖某一个类,它可以依赖于Car接口
public class Driver{
private Car car;
public void setCar(Car car){
this.car=car;
}
public void driver(){
car.run();
}
}
简而言之,是车就能开,具体开什么车***,通过暴露setCar方法让外部去决定。到这一步,我们做到了让司机类与具体某一种车的解耦。这一步非常关键,它在满足依赖倒置原则的同时,也体现了IoC!用人话来讲就是,司机类本应该自己new出来具体的Car实现类对象,自己掌握其创建和销毁等控制权,现在却通过setCar方法转交给外部来帮他选择具体开什么车,这种对依赖的控制权移交行为,已经是广义上的IoC了,换句话说,以前开什么车司机类说了算,现在它只能听外部(第三方)安排,这!就!是!IoC!***
现在我们要怎么使用司机类呢?
public class Test{
public static void main(String[] args){
Driver laowang=new Driver();
Car benz= new Benz();
laowang.setCar(benz);
laowang.drive();
}
}
如上图,我们的测试类中在实例化司机类后,需要通过setter方法为司机的对象注入它所依赖的具体车型,这一行为,就是依赖注入(DI),所以说依赖注入是实现IoC的一种方法,依赖注入有三种方式,如图是setter方式,还有构造器方式和接口方式,诸位自查。
此时,司机类将对车型的控制权交给了第三方,这个第三方在本例子中是MainTest,也可以是配置文件,或者IoC容器。
那么如果司机类的依赖对象不止交通工具一项呢?如果是100项呢?那我在实例化司机类的时候,是不是要为它set100种依赖对象进去?如果有一百个地方要用到司机类呢?那岂不是会写大量的重复性代码,头皮发麻!
于是乎,以Spring为代表的IoC容器闪亮登场,它为我们管理这些对象间的依赖,帮我们做这些重复性的工作,帮我们管理对象的生命周期,IoC容器说白了主要就是个帮我们DI的框架,(也提供了DL的实现)主要工作就是帮我们DI(本文中的setCar),所以说,并不是离了IoC容器就不算IoC了,钢笔写出来的是字,木棍杵出来的也是字,容器也好框架也罢,只是工具而已,思想和原理是走在前面的!