软件设计原则之《依赖倒置原则》

    今天我们聊一聊依赖倒置原则。如果仅仅从名字上理解该原则,很难。该原则给出的定义是:高层模块不应该依赖低层模块,二者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象针对接口编程,不要针对实现编程

    我在这里先把依赖倒置原则的优点写一下:可以减少类间的耦合性,提高系统稳定性,提高代码可读性和可维护性,可降低修改程序造成的风险。

    仅仅从这些字面含义来理解是很抽象的,我们一起写一写代码就能很容易理解该原则了。假如有这样一个场景,Jason同学读大学,由于他对Java课程和FE课程感兴趣,所以在学校里他选了Java课程,FE课程。代码如下:

package com.zybank.design.principle.dependeceinversion;

public class Jason {
    public void studyJavaCourse(){
        System.out.println("Jason学习Java课程");
    }
    public void studyFECourse(){
        System.out.println("Jason学习FE课程");
    }

}
package com.zybank.design.principle.dependeceinversion;

public class Test {
    public static void main(String[] args){
        Jason jason  = new Jason();
        jason.studyJavaCourse();
        jason.studyFECourse();
    }
}

    现在是Jason同学在校园里通过了解发现,python课程的学习对未来就业更有前景一些,他现在要选修python课程。这样我们是不是要在Jason类中修改代码,添加studyPythonCourse()方法啊,然后在应用层Test类中调用,如果是这种情况的话,我们的做法就是面向实现编程。如果我们面向实现编程,我们会面临最重要的一个问题就是实现类经常被修改,扩展性比较差。

    现在我们要引入抽象,以解决这个问题。代码如下:

package com.zybank.design.principle.dependeceinversion;

public interface Course {
    void studyCourse();
}
package com.zybank.design.principle.dependeceinversion;

public class JavaCourse implements Course{
    @Override
    public void studyCourse() {
        System.out.println("Jason学习Java课程");
    }
}
package com.zybank.design.principle.dependeceinversion;

public class FECourse implements Course {
    @Override
    public void studyCourse() {
        System.out.println("Jason学习FE课程");
    }
}

以上是们新增的类,那么Jason类我们修改如下:

package com.zybank.design.principle.dependeceinversion;

public class Jason {
    public void study(Course course){
        course.studyCourse();
    }

}

   具体学习什么课程,而是在应用层Test类中决定, 而我们的Test修改如下:

package com.zybank.design.principle.dependeceinversion;

public class Test {
    public static void main(String[] args){
        Jason jason = new Jason();
        jason.study(new JavaCourse());
        jason.study(new FECourse());
    }
}

在这种情况下,如果Jason同学在学习Python课程可以如下实现:

package com.zybank.design.principle.dependeceinversion;

public class PythonCourse implements Course {
    @Override
    public void studyCourse() {
        System.out.println("Jason学习Python课程");
    }
}

我们只需在Test类中添加一个调用即可。代码如下:

package com.zybank.design.principle.dependeceinversion;

public class Test {
    public static void main(String[] args){
        Jason jason = new Jason();
        jason.study(new JavaCourse());
        jason.study(new FECourse());
        jason.study(new PythonCourse());
    }
}

此时,UML类图如下:

熟悉UML类图的你,应该对这个图不是很陌生。Course接口此时是作为一个方法的参数传入给Jason类的study方法的。当然我们也可以通过构造器的方式注入。代码如下:

package com.zybank.design.principle.dependeceinversion;

public class Jason {
    private Course course;
    public Jason(Course course) {
        this.course = course;
    }

    public void study(){
        course.studyCourse();
    }

}
package com.zybank.design.principle.dependeceinversion;

public class Test {
    public static void main(String[] args){
        Jason jason = new Jason(new JavaCourse());
        jason.study();
        Jason jason1 = new Jason(new FECourse());
        jason1.study();
    }
}

而此时的构造器方式还不是最好的,因为此时如果想学python课程,在应用层的Test类中还比如new一个新的Jason实例。导致这种为题出现的原因是因为在Jason类中,我们并没有开放对Course变量的注入,我们添加一个setter方法进行注入,去掉构造方法的注入。代码如下:

package com.zybank.design.principle.dependeceinversion;

public class Jason {
    private Course course;
    
    public void setCourse(Course course) {
        this.course = course;
    }

    public void study(){
        course.studyCourse();
    }

}
package com.zybank.design.principle.dependeceinversion;

public class Test {
    public static void main(String[] args){
        Jason jason = new Jason();

        jason.setCourse(new JavaCourse());
        jason.study();

        jason.setCourse(new FECourse());
        jason.study();

        jason.setCourse(new PythonCourse());
        jason.study();

    }
}

此时的UML类图如下:

总结:我们在Test类中,不管想学什么课,都可以在不动Jason类的前提下,任意的修改。如果还想在学习算法课,只需要在低层进行扩展,而不需要动其他的类。依赖倒置的原则就是高层次的模块不依赖于低层次的模块。相对于细节的多变性,抽象的事物要稳定的多,以抽象搭建起来的架构比以细节搭建起来的架构要稳定。我们抽象的目的是制定好规范和契约。所以我们在日常的系统开发中要面向接口面向接口编程,面向接口编程,面向接口编程。

猜你喜欢

转载自blog.csdn.net/zhengzhoudaxuexujay/article/details/84982163