夯实设计原则之依赖倒置原则
作者:互联网
All Rights Reserved © jackiegu.cn
理念:
抽象不应该依赖于细节,细节应当依赖于抽象;也就是说要针对接口编程,而不是针对实现编程;
依赖倒置原则要求我们在定义方法的参数和返回值时尽量引用层次高的抽象层类(如:接口或者抽象类),同时一个具体类应当只实现接口或者抽象类中声明过的方法,而不要给出多余的方法,否则通过接口或者抽象类将无法调用到在子类中增加的新方法;
在实现依赖倒置原则时,我们需要针对抽象层编程,而将具体类的对象通过依赖注入的方式注入到其他对象中;依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象,常用的注入方式有三种,分别是:构造注入,设值注入、接口注入;
- 构造注入:是指通过构造函数来传入具体类的对象;
- 设值注入:是指通过Setter方法来传入具体类的对象;
- 接口注入:是指通过在接口中声明的参数来传入具体的对象;
案例:
背景情况是这样的:一个高科技公司,有一套自动驾驶技术,现该公司与汽车厂商奥迪和宝马达成了合作协议,需要将该自动驾驶技术移植到奥迪和宝马生产的车上,小明正好负责该项目,于是他是这样设计的代码:
首先定义了奥迪汽车AudiCar
和宝马汽车BwmCar
两个类,并同时拥有驾驶drive
、转向turn
、制动brake
这几种行为,如下:
/**
* 奥迪汽车
*/
public class AudiCar {
public void drive() {
System.out.println("奥迪汽车正在被驾驶");
}
public void turn() {
System.out.println("奥迪汽车正在被转向");
}
public void brake() {
System.out.println("奥迪汽车正在被制动");
}
}
/**
* 宝马汽车
*/
public class BmwCar {
public void drive() {
System.out.println("宝马汽车正在被驾驶");
}
public void turn() {
System.out.println("宝马汽车正在被转向");
}
public void brake() {
System.out.println("宝马汽车正在被制动");
}
}
然后他定义了一个自动驾驶类AutoDriveSystem
,如下:
/**
* 自动驾驶系统
*/
public class AutoDriveSystem {
private AudiCar audiCar = new AudiCar();
private BmwCar bmwCar = new BmwCar();
private String carName;
public AutoDriveSystem(String carName) {
this.carName = carName;
}
public void drive() {
if ("audi".equals(carName)) {
audiCar.drive();
} else if ("bmw".equals(carName)) {
bmwCar.drive();
} else {
System.out.println("no car drive");
}
}
public void turn() {
if ("audi".equals(carName)) {
audiCar.turn();
} else if ("bmw".equals(carName)) {
bmwCar.turn();
} else {
System.out.println("no car turn");
}
}
public void brake() {
if ("audi".equals(carName)) {
audiCar.brake();
} else if ("bmw".equals(carName)) {
bmwCar.brake();
} else {
System.out.println("no car brake");
}
}
}
当奥迪汽车需要自动驾驶了,如下:
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
AutoDriveSystem audiCarAutoDriveSystem = new AutoDriveSystem("audi");
audiCarAutoDriveSystem.drive();
audiCarAutoDriveSystem.turn();
audiCarAutoDriveSystem.brake();
}
}
// 运行结果
奥迪汽车正在被驾驶
奥迪汽车正在被转向
奥迪汽车正在被刹车
随着公司业务的做大,现在与奔驰厂商达成了合作协议,也需要该自动驾驶技术移植到奔驰生产的车上,于是小明新增了一个奔驰汽车BenzCar
类,如下:
/**
* 奔驰汽车
*/
public class BenzCar {
public void drive() {
System.out.println("奔驰汽车正在被驾驶");
}
public void turn() {
System.out.println("奔驰汽车正在被转向");
}
public void brake() {
System.out.println("奔驰汽车正在被制动");
}
}
然后修改了一下自动驾驶类AutoDriveSystem
,如下:
/**
* 自动驾驶系统
*/
public class AutoDriveSystem {
private AudiCar audiCar = new AudiCar();
private BmwCar bmwCar = new BmwCar();
private BenzCar benzCar = new BenzCar();
private String carName;
public AutoDriveSystem(String carName) {
this.carName = carName;
}
public void drive() {
if ("audi".equals(carName)) {
audiCar.drive();
} else if ("bmw".equals(carName)) {
bmwCar.drive();
} else if ("benz".equals(carName)) {
benzCar.drive();
} else {
System.out.println("no car drive");
}
}
public void turn() {
if ("audi".equals(carName)) {
audiCar.turn();
} else if ("bmw".equals(carName)) {
bmwCar.turn();
} else if ("benz".equals(carName)) {
benzCar.turn();
} else {
System.out.println("no car turn");
}
}
public void brake() {
if ("audi".equals(carName)) {
audiCar.brake();
} else if ("bmw".equals(carName)) {
bmwCar.brake();
} else if ("benz".equals(carName)) {
benzCar.brake();
} else {
System.out.println("no car brake");
}
}
}
从上面的案例来看,随着时间的推移,该公司可能会有更对的汽车厂商加入进来,那么自动驾驶类AutoDriveSystem
将会充斥着大量的if/else语句,导致异常的庞大,而且每次新增汽车厂商,都不得不去修改AutoDriveSystem
源码,违反了开闭原则;
同样是上面的案例,当懂得依赖倒置原则的小红遇到时,会怎样来设计呢?接下来请看:
首先定义一个汽车行为接口ICar
,如下:
/**
* 汽车行为接口
*/
public interface ICar {
void drive();
void turn();
void brake();
}
然后将所有汽车厂商生产出来的汽车都实现该接口,如以下的奥迪和宝马:
/**
* 奥迪汽车
*/
public class AudiCar implements ICar {
@Override
public void drive() {
System.out.println("奥迪汽车正在被驾驶");
}
@Override
public void turn() {
System.out.println("奥迪汽车正在被转向");
}
@Override
public void brake() {
System.out.println("奥迪汽车正在被刹车");
}
}
/**
* 宝马汽车
*/
public class BmwCar implements ICar {
@Override
public void drive() {
System.out.println("宝马汽车正在被驾驶");
}
@Override
public void turn() {
System.out.println("宝马汽车正在被转向");
}
@Override
public void brake() {
System.out.println("宝马汽车正在被刹车");
}
}
然后重新定义自动驾驶类AutoDriveSystem
,让其不再依赖于细节,而依赖于接口,如下:
/**
* 自动驾驶系统
*/
public class AutoDriveSystem {
private ICar car;
public AutoDriveSystem(ICar car) {
this.car = car;
}
public void drive() {
car.drive();
}
public void turn() {
car.turn();
}
public void brake() {
car.brake();
}
}
当奥迪汽车需要自动驾驶了,如下:
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
ICar audiCar = new AudiCar();
AutoDriveSystem audiCarAutoDriveSystem = new AutoDriveSystem(audiCar);
audiCarAutoDriveSystem.drive();
audiCarAutoDriveSystem.turn();
audiCarAutoDriveSystem.brake();
}
}
// 运行结果
奥迪汽车正在被驾驶
奥迪汽车正在被转向
奥迪汽车正在被刹车
从上面小红的代码来看,如果以后公司与其他汽车厂商达成了合作协议,那么直接让汽车公司生产的汽车实现ICar
接口,然后将其汽车实例通过构造注入方式注入到自动驾驶类AutoDriveSystem
中,即可达到自动驾驶功能;在这期间并不会去修改自动驾驶类AutoDriveSystem
,遵循了开闭原则,整个案例还遵循了里氏替换原则,以及本案主题的依赖倒置原则;
其实在大多数情况下,这三个设计原则会同时出现,开闭原则是目标,里氏替换原则是基础,依赖倒置原则是手段,它们相互想成,相互补充,目标一致,只是分析问题时所站角度不同而已;
标签:原则,void,System,夯实,carName,println,倒置,public,out 来源: https://blog.csdn.net/gu19930914/article/details/116931748