关于依赖注入、控制反转
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
在我看来,这句话已经很好的体现了出来IOC思想在编程中的体现形式,而采用这样的思想,可以大大降低类与类之间的耦合度,便于代码的扩展。
这里,我们看一个简单的例子:
我们假设有人和车,他们的类分别如此:
public interface Car {
public void run();
}
public interface Person {
public void Travel();
}
首先定义两个接口,表示要实现的车的功能和人的功能,而在实现人的功能时,旅游总不能光着脚出去吧,假设我们需要一辆车才能出行:
public class BenzCar implements Car{
@Override
public void run() {
System.out.println("奔驰车在跑");
}
}
public class Man implements Person {
private Car car;
public Man(){
car = new BenzCar();
}
@Override
public void Travel() {
System.out.println("我有车了");
car.run();
}
}
这是对应实现好的人类和车类,其中人类依赖于车类,他现在要自己创建一个车的对象,才能实现对应的功能。
我们测试一下这个代码可不可以运行:
public class Test {
public static void main(String[] args) {
Person person = new Man();
person.Travel();
}
}
执行以后,代码可以正常运行,但现在有一个问题,如果这个人过几天有钱了,不想用奔驰了,想换车,那怎么办呢。现在的代码,有一个很明显的问题,就是人和车类的耦合度太高,其原因就是在Man类的内部,自己创造了对应车类的对象,这就导致如果要想修改,只能对Man类的源码进行修改,这也就不符合“对修改关闭,对扩展开放”的原则了。
对这个问题的解决办法就是:不要从内部创建对象,而是由外部传入对象,你只要接收就可以了。
在更改后的代码为:
public class Man implements Person {
private Car car;
public Man(Car car){
this.car = car;
}
@Override
public void Travel() {
System.out.println("我有车了");
car.run();
}
}
将对应的Car类对象的生成方法,由自己new出来,变成通过构造参数传入(当然传入的方法不止一种,像set之类的方法也是可以的,这里只以构造方法为例)
现在对于Man类的扩展问题,我们已经解决了,就算他换成什么车,我们都可以直接在main函数中创建对应的对象,然后直接传入即可。
做到这里,像我这样从来没有大型编程经验的人就觉得已经很好了呀,我不用修改源代码,只要自己在主函数中创建对象,通过我设定的构造器呀set呀等方法传入就可以让程序正常运行啦。
但有多年开发经验的程序员们觉得还不大行,确实这样可以满足了代码复用和开闭原则,但你这样做有一个前提条件,就是你自己熟悉自己所写的全部类,可以明确他们的关系,才能够创建出对应的实体类,但如果是多人开发呢,如果一个人只负责一个模块,那会发生什么样的情况。
从上面我们可以看到,创建一个人的对象,我们需要显式地注入Car的对象,我们很清楚这一点,所以我们可以创造出man类的对象,但如果是多人合作,比如我是写service层的人员,我想调用person的travel()接口,但发现没有实体类,我需要构建。我new一个实体类,结果编译器报错误,我找了半天没找出来哪里有错,最后对Dao管理的人给我说,这个你要传进去一个Car类的对象,其中Car类对象有.xxx,sss之类。
我只是需要调用对象的接口,但这样的注入方法,让我必须清楚对象是如何创建的,而且还要为这个构建去new一个Car对象,这些都是完全不应该由我这样一个写service层的人来做的工作,为了解决这个问题,我们可以采用工厂模式。
先创建一个单例的工厂:
public class Factory {
private Factory() {
} public static Factory getInstance() {
return inner.instance;
} private static class inner {
private static final Factory instance = new Factory();
} }
在工厂中,我们可以创建Man对象:
public Person getMan(){
return new Man(new BenzCar());
}
这样,我们在外部调用的时候,只需要获得工厂,然后通过工厂的方法得到所需对象了,不再需要关心对象到底是如何生成的了。
public static void main(String[] args) {
Factory factory = Factory.getInstance();
Person person = factory.getMan();
person.Travel();
}
原本是调用者直接创建对象,现在改成了通过工厂这个容器,来直接获取对象。
这样,通过工厂方式+注入对象的方法,我们实现了类与类之间的解耦,也完成了生产时调用者只需使用类,而不用关心创建类的功能。
下一篇,我们了解一下在Spring中,对象是如何注入的。