IOC(Inversion of Control)其实是一种思想,这种思想并不是Spring独有的,而是在软件开发中,大家提出的一种开发原则,类似面向接口编程原则、开闭原则等。
网上有很多类似的文章尝试去通俗易懂地解释IOC思想,这里我根据自己的理解尝试去解释一下这个概念,看下面这个类:
public class Car {
private Wheel wheel;
/**
* Car类的run方法,其实是需要调用Wheel的turn方法来完成
* 汽车的奔跑是依赖轮子的转动
*/
public void run(){
wheel.trun();
}
}
public interface Wheel {
void trun();
}
Car类run()方法的执行,是依赖另外一个接口Wheel,这里肯定有小伙伴问,为什么Wheel是定义成一个接口呢?这其实是我们Java编程的另外一个原则的体现——面向接口编程。run()方法的内容,并不关心是什么轮子来转动,只知道轮子能转就行,至于是前驱轮子转、后驱轮子转,还是前后驱都转,Car类并不关心。换句话说,Wheel接口的实现类是什么,Car类并不关心,所以没有必要在Car类中定义具体的实现,只需要定义一个Wheel接口就可以了。
现在想要实现面向接口编程我们只差Wheel接口的实现类了:
/**
* 前驱轮的实现
*/
public class PrecursorWheel implements Wheel{
@Override
public void trun() {
System.out.println("precursor trun!");
}
}
我们新建了一个 Wheel接口 的实现类PrecursorWheel。但是这样很明显还不够,因为目前Car 类的 wheel成员变量还是个空引用,并没有关联PrecursorWheel的实现类呢。于是我们第一反应是这样改代码:
public class Car { private Wheel wheel; /** * Car类的run方法,其实是需要调用Wheel的turn方法来完成 * 汽车的奔跑是依赖轮子的转动 */ public void run(){ wheel = new PrecursorWheel();//wheel指向PrecursorWheel前驱轮实现类 wheel.trun(); } }
然后我们可以实例化一个Car类,然后让Car真正跑起来了,写个测试类(main方法就行)测一下:
public static void main(String[] args) {
Car car = new Car();
car.run();
}
测试结果就不贴了,Java新手脑补也能知道,But,如果这时候,客户说我不想要 前驱的车了,我要个后驱的车,咋办咧?简单!增加一个实现类:
/**
* 后驱轮子的实现类
*/
public class RearWheel implements Wheel{
@Override
public void trun() {
System.out.println("rear trun!");
}
}
然后改造Car类:
public class Car { private Wheel wheel; /** * Car类的run方法,其实是需要调用Wheel的turn方法来完成 * 汽车的奔跑是依赖轮子的转动 */ public void run(){ wheel = new RearWheel();wheel指向RearWheel后驱轮实现类 wheel.trun(); } public static void main(String[] args) { Car car = new Car(); car.run(); } }
这样Car类的实例,就变成了后轮驱动的Car对象了。
上面的编码风格用其特点就是如下:
目标类(Car类)中直接实例化其依赖接口的实现类(RearWheel、PrecursorWheel);
虽然我们的例子中只有 Car类依赖Wheel接口,但以后可能会有Truck类、Bicycle类....等很多类依赖Wheel接口,Wheel接口的实现类有一点点变动,依赖于它的类都要相应修改,这是牵一发而动全身的修改。
显然,问题就出在 Car 类中 这句代码上:wheel = new RearWheel();
这种硬编码的方式,让Car类与RearWheel类耦合了。
那么如何处理这样讨人厌的代码呢?大家就说,那就在程序种单独找一个第三方来管理类似这种wheel = new RearWheel()代码吧,这种思想就叫 IOC。
抽象总结出来就是:某一接口(如Wheel接口)具体实现类的选择权,从目标依赖类(如Car类)中移除,交由第三方来完成。这种思想就称为 IOC。 本例子中实现如下:
新建一个第三方类WheelManager,来专门管理 Wheel接口的实现类实例,所有与Wheel接口依赖的类,都通过第三方来得到想要的实例:
public class WheelManager {
public static Wheel acquireWheel(String name){
if("rear".equals(name)){
return new RearWheel();
}
return new PrecursorWheel();
}
}
Car类改造:
public class Car {
private Wheel wheel;
/**
* Car类的run方法,其实是需要调用Wheel的turn方法来完成
* 汽车的奔跑是依赖轮子的转动
*/
public void run(){
wheel = WheelManager.acquireWheel("rear");
wheel.trun();
}
}
如果还有 Truck类、Bicycle类,也类似:
public class Truck {
private Wheel wheel;
public void truckRun(){
wheel = WheelManager.acquireWheel("");
wheel.trun();
}
}
public class Bicycle {
private Wheel wheel;
public void bicycleRun(){
wheel = WheelManager.acquireWheel("rear");
wheel.trun();
}
}
上述就是一种简单的 IOC思想(控制反转)的实现方式:WheelManager变成了轮子的管理中心,所有Wheel接口的实现类实例,都交由它来管理。Car、Truck、Bicycle等具体的业务类,都通过WheelManager间接来与其要依赖的类进行耦合。
对控制反转的理解:
控制:指定Car类的Wheel成员变量是Wheel接口哪个实现类的实例,这个控制权。
反转:上述的控制权,不在Car类定义中去指定,而是交由第三方(WheelManager)来决定。