依赖倒置原则讲解

现在我们来学习依赖倒置原则,首先我们来看一下他的定义,高层模块不应该依赖底层模块,两者都应该依赖其抽象,

抽象不应该依赖细节,细节应该依赖于抽象,针对接口编程,而不要针对实现编程,那我们使用抽象包括引用接口,

或者抽象类,可以使各个类,模块的实现彼此独立,不影响,从而实现模块间的松耦合,降低模块间的耦合性,那我们在使用

依赖倒置原则的时候,还有一些要注意的点

比如每个类尽量继承自接口,或抽象类,这里面就说的比较泛化,也就是说你可以通过implements,实现接口,

通过一个extends继承一个抽象类,或者继承抽象类继承接口,这个只是说尽量,尽量避免具体的类派生,

尽量覆盖其方法,可以减少类间的耦合性,提高系统的稳定性,提高代码的可读性,和可维护性,并且最重要的,

还可以降低程序的风险,那依赖倒置原则也非常的简单,我们通过coding就可以非常容易的理解他了,

那现在我们一起来coding吗,那我们先创建一个包,主要目的是降低耦合,首先我们有这样一个场景,

我学习,可能对JAVA的课程比较感兴趣,另外我对前端的课程也比较感兴趣

package com.learn.design.principle.dependenceinversion;

/**
 * 我们来实现两个方法
 * 我们的问题就是
 * 假设我们现在要学习Python的课程
 * 那么我们在这里加
 * 实体的方法是在不断的补充的
 * 我们写一个python
 * 这里面我们来体会一下
 * 现在我们的做法就是面向实现编程
 * 因为整个Geely就是一个实现类
 * 我们在面向实现编程
 * 会发现一个最重要的问题
 * 整个实现类是需要经常修改的
 * 扩展性比较差
 * 也就是说我们应用层的函数
 * 
 * 我们现在看一下Geely这个类
 * 现在这三个具体的实现就可以干掉了
 * 我们看一下这个场景
 * 大家就比较熟悉了
 * 
 * 当然我们也可以通过构造器的方式来注入
 * Geely里面具体的实现
 * 然后我们在类里面声明一个iCourse
 * 用private也OK
 * 在这里是不是很熟悉
 * 比如对应的一个注入
 * 然后this.iCourse = iCourse赋值
 * 这个时候studyImoocCourse就不需要参数了
 * 我只要调用类里面的成员变量就可以了
 * 这个就是第三个版本了
 * 构造器注入
 * 
 * 构造器使用起来还不是太方便
 * 如果Geely想学别的
 * 我们还需要去new一个Geely
 * 因为这个只能在构造的时候才能注入进去
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Geely {

	/**
	 * 现在我们把它开放出去
	 * setter注入的方式
	 * 
	 * 
	 * @param iCourse
	 */
    public void setiCourse(ICourse iCourse) {
        this.iCourse = iCourse;
    }

    private ICourse iCourse;


    /**
     * 我们实现一个方法
     * 我们通过一个方法传一个对象
     * 而这个对象是需要用接口的
     * 因为我具体传FE课程还是JAVA课程
     * 是需要依据高层模块Test
     * 
     */
    public void studyImoocCourse(){
    	/**
    	 * 这里只需要iCourse调用studyCourse就可以了
    	 * 学习课程的时候
    	 * 具体的实现交给高层的模块
    	 * 而不是根据实现类来编写
    	 * 
    	 * 
    	 */
        iCourse.studyCourse();
    }

}
package com.learn.design.principle.dependenceinversion;

/**
 * 我们再来一个FECourse
 * 实现ICourse
 * 包不要选错
 * 
 * 
 * @author Leon.Sun
 *
 */
public class FECourse implements ICourse {
	/**
	 * 实现他的方法
	 * 
	 */
    @Override
    public void studyCourse() {
        System.out.println("Geely在学习FE课程");
    }

}
package com.learn.design.principle.dependenceinversion;

/**
 * 首先我们创建一个类
 * 用接口Icourse
 * 还是课程
 * 
 * 
 * @author Leon.Sun
 *
 */
public interface ICourse {
	/**
	 * 这个课程有个方法studyCourse
	 * 具体学什么课程
	 * 我们交给高层的应用层来选择
	 * 接下来也很简单
	 * 
	 */
    void studyCourse();
}
package com.learn.design.principle.dependenceinversion;

/**
 * JavaCourse实现ICourse这个课程类
 * 因为在开闭原则里面
 * 也使用了同样的类名
 * 选择我们刚刚创建的这个类
 * 现在实现了这个类
 * 
 * 
 * @author Leon.Sun
 *
 */
public class JavaCourse implements ICourse {

    @Override
    public void studyCourse() {
        System.out.println("Geely在学习Java课程");
    }
}
package com.learn.design.principle.dependenceinversion;

/**
 * 实现ICourse
 * 这样我们就实现了学习Python的一个类
 * 
 * @author Leon.Sun
 *
 */
public class PythonCourse implements ICourse {
    @Override
    public void studyCourse() {
        System.out.println("Geely在学习Python课程");
    }
}
package com.learn.design.principle.dependenceinversion;

public class Test {

	/**
	 * 我们的问题就是
	 * 假设我们现在要学习Python的课程
	 * 
	 * 因为TEST这个类是应用层的
	 * 它是属于高层模块
	 * Geely是属于低层模块
	 * 因为现在只有这两个类
	 * 根据依赖倒置的原子性
	 * 高层次的模块是不应该依赖于低层次的模块的
	 * 也就是说Test里面的实现
	 * 现在依赖于Geely的具体实现
	 * 里面实现什么
	 * 我都要来扩展
	 * 然后再高层模块才可以使用
	 * 那我们现在引入抽象
	 * 看看如何来解决这个问题
	 * 
	 * 这里认为是一个v1版本
	 * 放到这儿
	 * 这样的话也有个对比
	 * 
	 * @param args
	 */
    //v1
//    public static void main(String[] args) {
//        Geely geely = new Geely();
//        geely.studyJavaCourse();
//        geely.studyFECourse();
//    }

	/**
	 * 还是new一个Geely
	 * 这里面new一个JavaCourse
	 * 再new一个FECourse
	 * 输出结果是一样的
	 * 但是写法是不一样的
	 * 我们看一下类图
	 * 首先ICourse接口放在这里边
	 * 如果有其他课程的实现
	 * 例如Python的课程
	 * 和他们平级放在这里面
	 * 而具体的Geely这个类
	 * 不需要动的
	 * 就是说我们要面向接口编程
	 * 我去写的这个扩展类
	 * 是面向接口的
	 * 而不是面向具体的Geely这个实现类
	 * 对于高层模块
	 * 具体我们学习什么课程
	 * 例如Python
	 * 是让高层模块来选择
	 * 这样的话就做到了
	 * Geely和Test两个之间是解耦的
	 * 同时Geely和课程的具体实现是解耦的
	 * 但是他和ICourse是耦合的
	 * 所谓的高内聚和低耦合
	 * 也就是尽量减少耦合
	 * 因为有依赖关系
	 * 例如ICourse是Geely这个类的
	 * 一个方法中的参数
	 * 所以他两之间是有关系的
	 * 所以看到这个类图我们就想
	 * 接下来的扩展就非常简单了
	 * 例如我现在还想学习Python的课程
	 * 我们来创建爱一个类
	 * 而对于高层模块
	 * 应用层的Test类
	 * 我们通过geely.studyImoocCourse
	 * 里面的参数注入进去
	 * 这样的话我们看一下结果
	 * 对于新的课程
	 * 我们只需要新建一个类
	 * 然后实现这个接口
	 * 面向接口编程
	 * 然后具体实现哪个课程
	 * 交给应用层的Test类
	 * 高层模块
	 * 这个是通过接口方法的方式
	 * 来注入具体的实现
	 * 
	 * v2是接口方法注入的方式
	 * 
	 * 
	 * 
	 * 
	 * @param args
	 */
    //v2
//    public static void main(String[] args) {
//        Geely geely = new Geely();
//        geely.studyImoocCourse(new JavaCourse());
//        geely.studyImoocCourse(new FECourse());
//        geely.studyImoocCourse(new PythonCourse());
//    }

	/**
	 * Geely中new一个Geely
	 * 里面new一个JavaCourse
	 * geely.studyImoocCourse
	 * 这里就开始学课程了
	 * 是因为具体的ICourse的实现
	 * 构造器已经注入了
	 * 这是第三个版本
	 * 
	 * 构造器使用起来还不是太方便
	 * 如果Geely想学别的
	 * 我们还需要去new一个Geely
	 * 因为这个只能在构造的时候才能注入进去
	 * 因为是单例模式
	 * 如果在这里面还想学FE课程
	 * 现在只能new一个Geely出来
	 * 因为Geely类里面并没有对ICourse的一个注入
	 * 
	 * 
	 * @param args
	 */
    //v3
//    public static void main(String[] args) {
//        Geely geely = new Geely(new JavaCourse());
//        geely.studyImoocCourse();
//    }
	
	/**
	 * 接下来我们接着写他的实现
	 * 这样就不依赖于自己的构造器了
	 * 也就是Geely在具体执行的时候
	 * 学习课程
	 * 他不需要关心
	 * 也就是这个方法不需要关心
	 * 我学A的我学B的
	 * 比如第一种写法我要关心具体的实现
	 * 我要调用哪个方法呢
	 * 我要调用学习JAVA的方法
	 * 我要调用学习前端课程的方法
	 * 输出结果也是一样的
	 * 我们再看一下现在的类图
	 * 
	 * @param args
	 */
    public static void main(String[] args) {
        Geely geely = new Geely();
        geely.setiCourse(new JavaCourse());
        geely.studyImoocCourse();

        /**
         * 学习JAVA课程之后
         * 我还想学习前端的课程
         * 
         * 
         */
        geely.setiCourse(new FECourse());
        geely.studyImoocCourse();

    }


}

我们可以看到三个具体的实现,实现ICourse,Geely在这里面,应用层的类在这里面,底层模块和高层模块,不是说在

上面就是高,而是按照应用层,MVC架构里边,对上层我们接收请求的,可能是Controller,他下一层叫做Service层,

Service层去调用DAO层,service层相对于DAO层是高的,这里面对外的应用层肯定是Test,所以这里面我们要体会一下,

面向接口编程,和面向实现编程的最大区别,相信通过这个例子,我们对依赖倒置原则有深刻的理解,当然我们这里不需要

接口,用抽象类也可以,那抽象类也是一种抽象,Geely这个类是不依赖于具体的,是JavaCourse,还是FECourse,还是Python

Course,我们可以看到我们在Geely这个类里面根本没有导入他们的包,那其实我们在体会的时候呢,可以看import,

当然现在都处于同一个包下,不需要import,直接就能够引用过来,但是这个类里面并没有显性的去写JavaCourse,

PythonCourse,FECourse,也就是Geely想学什么课,我们都可以在不动Geely这个模块的前提下,任意的修改,

Test是应用层的,肯定是需要改的,主要是Geely,Geely是相对于ICourse,实现更高层的一个类,具体想学什么课,

这个类都不需要动,我只需要在底层模块进行扩展,注意扩展,我学什么课,不会修改JavaCourse,FECourse,和PythonCourse,

比如我先做学算法课,这个三个类是不需要动的,这也符合开闭原则,不能说我学个算法课,要在JavaCourse的实现里边,

各种写,很容易引入新的问题,依赖倒置的原则就是,高层次的模块不应该依赖于低层次的模块,那依赖倒置的原则,

表达出一个什么样的事实呢,抽象的东西要稳定的多,以抽象为基础,搭建起来的架构,比以细节搭建起来的,抽象的

目的就是制定规范,比如ICourse的一个接口,学习一个课程是一个契约,具体怎么实现交给具体的实现类,而Geely是

不依赖于具体课程的实现,因为现在是这几个种类,说不定,又出现什么新的语言了,那Geely这个类不需要动,

其他课程实现类也不需要动,只要新增实现就可以了,所以核心思想就是面向接口编程,一定要记住,面向接口编程,

那理解依赖倒置原则,理解Spring的依赖注入和控制反转,那就相当容易了,我们再看一下类图,希望理解依赖倒置原则

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值