• 设计模式(五)设计原则part2


    SOLID五种原则中前面我们学习了SOLI,那么就还剩下一种,D,也就是DIP,我们叫做依赖反转原则 Dependency Inverion Principle。看到这个名字是否很熟悉,因为我们的框架中经常提到的是,控制反转,依赖注入,而依赖反转是设计原则中的一种。

    所以我们结合控制反转和依赖注入来学习依赖反转。

    控制反转IOC

    提到控制反转和依赖注入对于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");
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    首先我们看上面这个类,对于上面的测试类中,所有的流程都由程序员在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();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • IOC测试类
    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();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    那么对于这个框架,我们可以对之前的UserServiceTest进行修改

    package DesignPrinciple.DIP;
    
    /**
     * @time: 2022/10/20
     * @author: yuanyongan
     * @description: IOC测试类
     */
    public class UserServiceTest extends IOCTestCase{
    
    
        @Override
        public boolean doTest() {
            return false;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这就是一个控制反转的例子,这里的控制其实是对程序执行流程的控制,在使用框架前,整个程序执行的流程其实都是程序员来决定并且进行代码的编写。而使用框架之后,整个程序的执行流程其实都在框架里面,程序员要对样例进行编写,只需要对照框架给出的拓展点,也就是IOCTestCase,进行需求代码的编写就行了。

    所以控制反转其实是一种比较笼统的设计思想。

    依赖注入 DI

    依赖注入Dependency Injection其实就是不通过new()的方法在类的内部创建当前类依赖的类对象。而是将依赖的类对象在外部创建后,通过构造函数,注解,函数参数等方式传递给类使用。

    这个其实就比较简单了,我们平时用的都比较多,而在Spring中就可以通过Autowired注解等方式对依赖进行注入,这个时候是不需要通过new()方法进行依赖的类对象进行创建的。

    依赖反转 DIP

    知道了控制反转和依赖注入,其实依赖反转就是两种的结合,一个是依赖,就是类与类之间的依赖关系,一个是反转,就是将依赖关系反转过来。这种设计原则可以让代码变的拓展性更强,具体是怎么实现的呢,下面通过一个例子进行学习。

    比如一个手机工厂,前几年是生产的是华为手机,

    
    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();
            //...生产手机
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    目前来看,这个依赖关系没什么问题,手机工厂依赖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();
            //...生产手机
        }
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    这个时候的依赖关系其实是这样的,这样的依赖关系其实就可以让我们的代码可拓展性更强。而这也是依赖反转原则的目的所在。将依赖的关系反转过来,让代码的可拓展性变强。
    在这里插入图片描述

    代码地址:https://gitlab.com/WyLmYYA
    欢迎交流

  • 相关阅读:
    Flink状态
    ES6数组的方法及伪数组转数组方法
    Vue 组件的全局注册与组件的jsx实现方法
    Git 内容学习
    LeetCode 387 字符串中的第一个唯一字符 简单
    基于信通院 Serverless 工具链模型的实践:Serverless Devs
    Hadoop笔记06-Hadoop-源码解析
    WPF RelativeSource属性-目标对象类型易错
    10、Spring 源码学习 ~ Bean 的加载步骤详解(一)
    js事件流
  • 原文地址:https://blog.csdn.net/qq_43176812/article/details/127429561