控制反转是一种思想,依赖注入是控制反转的一种实现方式。为了实现松耦合,我们采取了控制反转的思想。
什么是依赖?
如果一个类A的功能实现要借助B,那么就叫B是A的依赖,则称作A依赖于B。比如说,一个人喜欢看视频,而他看视频需要借助手机来实现,则称他依赖于手机。
依赖倒置
依赖倒置原则:
- 上层模块不应该依赖底层模块,它们都应该依赖于抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象
在实际开发中,对于依赖,我们会这样处理:
public class Person {
private Bike mBike;
public Person() {
mBike = new Bike();
}
public void chumen() {
System.out.println("出门了");
mBike.drive();
}
}
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person person = new Person();
person.chumen();
}
}
可是,假如我们想要开车出门了。
public class Person {
private Bike mBike;
private Car mCar;
public Person() {
//mBike = new Bike();
mCar = new Car();
}
public void chumen() {
System.out.println("出门了");
//mBike.drive();
mCar.drive();
}
}
这样,我们每次想要修改出门方式的时候,就要在Person类中修改大量的代码,当项目过于庞大时,会很难进行维护。我们应该遵循依赖倒置的原则。
- 上层模块不应该依赖底层模块,它们都应该依赖于抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象
上层模块不应该依赖于底层模块,而是依赖于抽象。
因此,我们创建一个接口。
public interface Driveable {
void drive();
}
各种交通工具,比如Car,bike,Train都去实现这个类。这样,Person应是这样。
public class Person {
// private Bike mBike;
// private Car mCar;
// private Train mTrain;
private Driveable mDriveable;
public Person() {
//mBike = new Bike();
//mCar = new Car();
//mTrain = new Train();
mDriveable = new Train();
}
public void chumen() {
System.out.println("出门了");
//mBike.drive();
//mCar.drive();
//mTrain.drive();
mDriveable.drive();
}
}
这样,每次我们交通工具改变的时候,只需要修改一行代码即可,大大方便。
上面的内容就是依赖倒置原则。
有人会考虑到倒置这个词,个人的理解是倒置是改变的意思。
本来正常编码下,肯定会出现上层依赖底层的情况,而依赖倒置原则的应用则改变了它们之间依赖的关系,它引进了抽象。上层依赖于抽象,底层的实现细节也依赖于抽象,所以依赖倒置我们可以理解为依赖关系被改变,如果非常纠结于倒置这个词,那么倒置的其实是底层细节,原本它是被上层依赖,现在它倒要依赖与抽象的接口。
控制反转 (IoC)
虽然,我们实现了依赖倒置,更改出行方式很方便,但是每次更改出行方式的时候,Person 这个类还是要修改。Person还是自己掌管着接口的实例化。这样,程序也就不方便维护。因此,我们将Person对Drivable的控制权剥离出来。
public class Person {
private Driveable mDriveable;
public Person(Driveable driveable) {
this.mDriveable = driveable;
}
public void chumen() {
System.out.println("出门了");
mDriveable.drive();
}
}
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Bike bike = new Bike();
Car car = new Car();
Train train = new Train();
// Person person = new Person(bike);
// Person person = new Person(car);
Person person = new Person(train);
person.chumen();
}
}
这里我们相当于把控制权交给了Test1这个类。也就是说 Person 只关心依赖提供的功能,但并不关心依赖的创建。Person 不再亲自创建 Driveable 对象,它将依赖的实例化的权力交接给了 Test1。
把A对B的控制权抽离出来,将控制权交给第三方,这就是控制反转。依赖注入是控制反转最典型的实现方法。将控制权交给IOC容器,通过构造方法,属性或者工厂模式来将类B注入到A中。
依赖倒置是面向对象开发领域中的软件设计原则,它倡导上层模块不依赖于底层模块,抽象不依赖细节。
依赖反转是遵守依赖倒置这个原则而提出来的一种设计模式,它引入了 IoC 容器的概念。
依赖注入是为了实现依赖反转的一种手段之一。
它们的本质是为了代码更加的“高内聚,低耦合”。
Spring IOC
平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。
总结
IOC意味着将你设计好的对象交给容器控制(IOC容器),而不是传统的在你的对象内部直接控制。
依赖注入是控制反转最典型的实现方法。将控制权交给IOC容器,通过构造方法,属性或者工厂模式来将类B注入到A中。