SOLID五种原则中前面我们学习了SOLI,那么就还剩下一种,D,也就是DIP,我们叫做依赖反转原则 Dependency Inverion Principle。看到这个名字是否很熟悉,因为我们的框架中经常提到的是,控制反转,依赖注入,而依赖反转是设计原则中的一种。
所以我们结合控制反转和依赖注入来学习依赖反转。
提到控制反转和依赖注入对于Java工程师而言,很容易联想到Spring框架,但这两个概念并不是Spring特有的。
package DesignPrinciple.DIP;
/**
* @time: 2022/10/20
* @author: yuanyongan
* @description: IOC测试类
*/
public class UserServiceTest {
public static boolean doTest(){
// ...业务测试逻辑
return true;
}
public static void main(String[] args) {
if (doTest()){
System.out.println("Test Succeed");
}else{
System.out.println("Test Failed");
}
}
}
首先我们看上面这个类,对于上面的测试类中,所有的流程都由程序员在doTest函数中进行控制。
当我们自己设计一个测试框架之后,代码如下:
package DesignPrinciple.DIP;
/**
* @time: 2022/10/20
* @author: yuanyongan
* @description:
*/
public abstract class IOCTestCase {
public void run(){
if (doTest()){
System.out.println("Test Succeed");
}else{
System.out.println("Test Failed");
}
}
public abstract boolean doTest();
}
package DesignPrinciple.DIP;
import java.util.ArrayList;
import java.util.List;
/**
* @time: 2022/10/20
* @author: yuanyongan
* @description: 控制反转的例子
*/
public class IOC {
private static final List<IOCTestCase> testCases = new ArrayList<>();
public static void register(IOCTestCase testCase){
testCases.add(testCase);
}
public void runTestCases(){
for (IOCTestCase testCase: testCases){
testCase.run();
}
}
}
那么对于这个框架,我们可以对之前的UserServiceTest进行修改
package DesignPrinciple.DIP;
/**
* @time: 2022/10/20
* @author: yuanyongan
* @description: IOC测试类
*/
public class UserServiceTest extends IOCTestCase{
@Override
public boolean doTest() {
return false;
}
}
这就是一个控制反转的例子,这里的控制其实是对程序执行流程的控制,在使用框架前,整个程序执行的流程其实都是程序员来决定并且进行代码的编写。而使用框架之后,整个程序的执行流程其实都在框架里面,程序员要对样例进行编写,只需要对照框架给出的拓展点,也就是IOCTestCase,进行需求代码的编写就行了。
所以控制反转其实是一种比较笼统的设计思想。
依赖注入Dependency Injection其实就是不通过new()的方法在类的内部创建当前类依赖的类对象。而是将依赖的类对象在外部创建后,通过构造函数,注解,函数参数等方式传递给类使用。
这个其实就比较简单了,我们平时用的都比较多,而在Spring中就可以通过Autowired注解等方式对依赖进行注入,这个时候是不需要通过new()方法进行依赖的类对象进行创建的。
知道了控制反转和依赖注入,其实依赖反转就是两种的结合,一个是依赖,就是类与类之间的依赖关系,一个是反转,就是将依赖关系反转过来。这种设计原则可以让代码变的拓展性更强,具体是怎么实现的呢,下面通过一个例子进行学习。
比如一个手机工厂,前几年是生产的是华为手机,
package DesignPrinciple.DIP;
/**
* @time: 2022/10/20
* @author: yuanyongan
* @description:
*/
public class Huawei {
public void getParameter(){
// ... 业务需求
}
}
package DesignPrinciple.DIP;
/**
* @time: 2022/10/20
* @author: yuanyongan
* @description:
*/
public class PhoneFactory {
private Huawei huawei;
public PhoneFactory(Huawei huawei){
this.huawei = huawei;
}
public void produce(){
huawei.getParameter();
//...生产手机
}
}
目前来看,这个依赖关系没什么问题,手机工厂依赖Huawei,但是如果有一天,这个工厂不生产Huawei手机了,而是生产Xiaomi呢,那依赖的Huawei是不是又得修改了,而这个修改工作是非常大的。依赖关系如下
那么依赖反转其实就是为了解决这个问题,基于依赖反转原则,我们可以创建一个新的类Phone,修改代码如下。
public abstract class Phone {
abstract void getParameter();
}
public class Huawei extends Phone{
public void getParameter(){
// ... 业务需求
}
}
public class PhoneFactory {
private Phone phone;
public PhoneFactory(Phone huawei){
this.phone = huawei;
}
public void produce(){
phone.getParameter();
//...生产手机
}
}
这个时候的依赖关系其实是这样的,这样的依赖关系其实就可以让我们的代码可拓展性更强。而这也是依赖反转原则的目的所在。将依赖的关系反转过来,让代码的可拓展性变强。
代码地址:https://gitlab.com/WyLmYYA
欢迎交流