一、序言
本文跟大家聊聊 IoC 这一简单而深邃的思想。
二、依赖倒置原则
软件工程理论中共有六大设计原则:
- 单一职责原则:不存在多于一个的因素导致类的状态发生变更,即一个类只负责一项单一的职责。
- 里氏替换原则:基类出现的地方都可以用其子类进行替换,而不会引起任何不适的问题。
- 接口隔离原则:客户端不应该依赖于其不需要的接口,类之间的依赖关系应该建立在最小的接口之上。
- 迪米特法则:一个对象对其他对象有最少的了解。
- 开闭原则:软件设计对于扩展是开发的,即模块的行为是可以扩展的。软件设计对于修改是关闭的,即模块的行为是不可修改的。
- 依赖倒置原则:高层次的模块不应该依赖于低层次的模块,都应该依赖于抽象。
今天我们只讨论依赖倒置原则。上面对于依赖倒置原则的绕口令我们先暂时放下。
场景假设:一个人通过一种交通工具上下班。
若不遵循依赖倒置原则,实现方案如下:
居住地离公司较近时:
// 自行车类 public class Bike { public void go() { System.out.println("骑自行车"); } } public class Person { // 自行车 private Bike bike; // 构造器 public Person() { this.bike = new Bike(); } // 上班 public void goToWork() { bike.go(); } }
居住地离公司较远时:
// 公交车类 public class Bus { public void go() { System.out.println("乘坐公交车"); } } public class Person { // 公交车 private Bus bus; // 构造器 public Person() { this.bus = new Bus(); } // 上班 public void goToWork() { bus.go(); } }
不遵循依赖倒置原则时,我们发现了一个巨大的问题:每换一次交通工具,Person 类几乎是重构了一遍。
遵循依赖倒置原则,实现方案如下:
交通工具会有很多,我们将其进行一次抽象:
// 交通工具接口 public interface Vehicle { // 出发 void go(); }
居住地离公司较近时:
// 自行车类 public class Bike implements Vehicle { @Override public void go() { System.out.println("骑自行车"); } } public class Person { // 此处放抽象接口 private Vehicle vehicle; // 构造器 public Person() { // 抽象接口的实现类是自行车 this.vehicle = new Bike(); } // 上班 public void goToWork() { vehicle.go(); } }
居住地离公司较远时:
// 公交车类 public class Bus implements Vehicle { @Override public void go() { System.out.println("乘坐公交车"); } } public class Person { // 此处放抽象接口 private Vehicle vehicle; // 构造器 public Person() { // 抽象接口的实现类是公交车 this.vehicle = new Bus(); } // 上班 public void goToWork() { vehicle.go(); } }
当我们采用依赖倒置原则实现方案时,可以发现除了构造器中需要修改,其他部分不需要做任何的修改。
现在我们重新看一下依赖倒置原则那段绕口令。高层次的模块不应该依赖于低层次的模块,即高层次模块与低层次模块应该解耦,就如同 Person 类和 Bike/Bus 类一样。如果 Person 类中直接依赖 Bike/Bus 类就会出现高耦合。都应该依赖于抽象指的是:为了解耦,高层次的模块应该依赖于低层次模块的抽象而不是具体的低层次模块。就如同 Person 类应该依赖 Vehicle 抽象接口。
三、IoC 思想
当我们遵循依赖倒置原则开发时虽然解决了大部分问题,但是我们发现依旧还存在一些问题:
public class Person {
private Vehicle vehicle;
public Person() {
// 此处每次仍需要修改
this.vehicle = new Bus();
}
public void goToWork() {
vehicle.go();
}
}
上面的问题是: Person 类依旧需要手动的创建 Bike 或 Bus 对象。有没有一种方式将这种手动的方式变成自动管理,从而进一步实现解耦呢?解决方案就是采用 IoC 思想。
IoC,全称为 Inversion of Control,中文名为“控制反转”,是一种设计思想。其核心思想是:将对象的创建权、管理权移交给容器,而不是开发人员。
Spring 已经将 IoC 思想具象化,我们以 Spring 中的 IoC 容器为例重新分析上述的例子。
// 自行车类
@Component
public class Bike implements Vehicle {
@Override
public void go() {
System.out.println("骑自行车");
}
}
@Component
public class Person {
// 此处放抽象接口
@Resource
private Vehicle vehicle;
// 构造器
public Person() {
}
// 上班
public void goToWork() {
vehicle.go();
}
}
从上面的代码中我们可以看出:在 Person 类中我们使用 @Resource
注解将 Vehicle 对象的赋值交由 IoC 容器。容器创建 Bike 对象之后将其引用赋给了上述的 vehicle 变量。至此,对象与对象之间的完全解耦实现了。
四、IoC 思想工作流程的具象化
- 开发人员向 IoC 容器声明需要的对象
- IoC 容器维护所有对象之间的关系,以及对象的创建时机
- 开发人员需要对象时直接从 IoC 容器中获取(获取到的对象与其他对象的关系已由 IoC 容器维护好)