定义
依赖倒置原则
要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
- 高层模块不应该依赖底层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 针对接口编程,不针对实现编程
里氏替换原则
任何基类可以出现的地方,子类一定可以出现,反之未必。
关于依赖倒置原则/里氏替换原则
由于两个原则很像,一个是面向接口抽象,一个是面向抽象类抽象。因此这里如果使用接口就是依赖倒置原则,如果将接口改成抽象类就是里氏替换原则。
通过代码理解
场景
当我们在上大学的时候,我们都有必修课,选修课,专业课等。那么假如让我们用JAVA去表达学习这几种课,我们该如何设计呢。
错误方式
1.定义个一个学生类,其中里面有学习必修课和选修课的方法。
/**
* @ClassName Student
* @Description 学生类
* @Author Neal
* @Date 2019/2/27 15:43
* @Version 1.0
*/
public class Student {
/**
* 学习选修课
*/
public void studyElectives() {
System.out.println("学生开始学习选修课.....");
}
/**
* 学习必修课
*/
public void studyCompulsorCourse() {
System.out.println("学生开始学习必修课......");
}
}
2.启动类
/**
* @ClassName WrongTest
* @Description 错误测试类
* @Author Neal
* @Date 2019/2/27 15:53
* @Version 1.0
*/
public class WrongTest {
public static void main(String[] args) {
Student student = new Student();
//学习选修
student.studyElectives();
//学习必修
student.studyCompulsorCourse();
}
}
这种方式就是典型的面向实现编程,为什么这么说的 ,因为我们上层的修改是依赖于底层实现的,也就是我们WrongTest#main
方法的实现是依赖于底层Student
中的方法的。比如说我们要学习选修课中的一门具体的课,比如(体育课),那么我们只能这么实现。在Student
类中修改studyElectives()
方法。
/**
* 学习选修课--体育课
*/
public void studyElectives() {
System.out.println("学生开始学习选修课---体育课");
}
这种方式s就违反了我们的 依赖倒置原则。那么什么方式是适合这种原则的呢。
正确方式
思路
- 创建一个课程接口,接口中有学习课程的方法。
- 创建选修课和必修课的实现类,去实现课程接口。
- 创建学生类,定义学生学习课程的方法,方法的入参就是课程接口,这样我们就隔离了学生学习的课程到底是选修课还是必修课这个过程。
- 然后选择一个具体的课程创建该课程的类,继承必修课或选修课的类,通过这个我们就进一步的隔离了具体课程的名称。
- 创建调用类(上层类)。
具体代码
1.课程接口:如果是接口interface
则就是依赖倒置原则),若为课程抽象类则就是里氏替换原则。以下就不多做解释。
/**
* @InterfaceName ICourse
* @Description 课程接口
* @Author Neal
* @Date 2019/2/27 16:10
* @Version 1.0
*/
public interface ICourse {
void studyCourse();
}
2.创建选修课和必修课的类
/**
* @ClassName CompulsorCourse
* @Description 必修课
* @Author Neal
* @Date 2019/2/27 16:12
* @Version 1.0
*/
public class CompulsorCourse implements ICourse{
public void studyCourse() {
System.out.println("学生开始学习必修课.....");
}
}
/**
* @ClassName ElectiveCourse
* @Description 选修课程
* @Author Neal
* @Date 2019/2/27 16:12
* @Version 1.0
*/
public class ElectiveCourse implements ICourse{
public void studyCourse() {
System.out.println("学生开始学习选修课.....");
}
}
3.创建学生类
/**
* @ClassName Student
* @Description 学生类
* @Author Neal
* @Date 2019/2/27 16:17
* @Version 1.0
*/
public class Student {
/**
* 定义了学习课程方法
* @param iCourse 课程接口
*/
public void studyCourse(ICourse iCourse) {
iCourse.studyCourse();
}
}
4.具体课程类
/**
* @ClassName JavaCourse
* @Description 学习JAVA必修课
* @Author Neal
* @Date 2019/2/27 16:15
* @Version 1.0
*/
public class JavaCourse extends CompulsorCourse {
@Override
public void studyCourse() {
System.out.println("学生学习JAVA必修课");
}
}
5.调用类(上层类)
/**
* @ClassName RightTest
* @Description TODO
* @Author Neal
* @Date 2019/2/27 16:17
* @Version 1.0
*/
public class RightTest {
public static void main(String[] args) {
//首先我们创建学生对象
Student student = new Student();
//学生去学习必修课。
CompulsorCourse compulsorCourse= new CompulsorCourse();
student.studyCourse(compulsorCourse);
//学生去学习选修课
ElectiveCourse electiveCourse = new ElectiveCourse();
student.studyCourse(electiveCourse);
//学生去学习具体的一门课程 比如JAVA
JavaCourse javaCourse = new JavaCourse();
student.studyCourse(javaCourse);
}
}
这个方式就是面向接口编程,我们可以基本的解释了依赖倒置原则,我们不需要依赖修改下层实现(Student#studyCourse()
),只需要更改上层应用就可以了。也就是我们想让学生学什么课程,只需要创建对应的对象,并调用底层Student#studyCourse()
方法就可以了。方法内部不需要做任何修改,就可以让学生学习任意的一门课程。
我们这里只是为了解释依赖倒置原则。其实实现上述方式总共有三种方式,感兴趣的同学可以自行实现或者百度。
- 接口声明依赖对象(已用)
- 构造函数传递依赖对象
- Setter方法传递依赖对象
总结
依赖倒置原则和里氏替换原则的根本就是面向接口(抽象)编程。通过接口制定契约,来约束实现类,这样无论应用层做如何的修改,底层的契约是不需要改变的。 文笔能力有限,希望大家可以理解依赖倒置原则。
网友评论