设计模式|设计原则之依赖倒置原则
记录对设计模式学习的过程
前言
-
定义:高层模块不应以依赖低层模块,二者都应依赖其抽象
-
抽象不应该依赖细节;细节应该依赖抽象
-
针对接口编程,不要针对实现编程
-
优点:低耦合 、、、、等等
详细概念可以网上查询
案例实验
有一个查询数据的类SearchDao.java
package com.dsdj.design.principle.dependenceinversion;
/**
* @ClassName SearchDao
* @Description TODO
* @Author dsdj
* @Date 2018/10/9 下午7:46
* @Version 1.0
**/
public class SearchDao {
public void getUsers(){
System.out.println("查询人员的功能");
}
public void getBooks(){
System.out.println("查询图书的功能");
}
}
测试类
package com.dsdj.design.principle.dependenceinversion;
/**
* @ClassName Test
* @Description TODO
* @Author dsdj
* @Date 2018/10/9 下午7:50
* @Version 1.0
**/
public class Test {
public static void main(String[] args) {
SearchDao searchDao = new SearchDao();
searchDao.getBooks();
searchDao.getUsers();
}
}
如果我们需要新增一个查询课程的类,只要再添加一个方法就好了,这个是典型的面向实现的编程。
新增需求,我们需要查找课程的集合,该怎么写呢?
需要在SearchDao中添加getCourse()的方法,然后在Test中重新调用getCourse()方法
是不是觉得这样做很麻烦?
问题来了
-
这个类需要经常修改,导致扩展性比较差
-
上面的例子,Test.java是高层次的类,SearchDao.java是低层次的类。根据依赖导致的原则,高层次的类是不依赖低层次的模块的。Test依赖SearchDao的具体实现。导致没增加一个功能,只能去底层模块添加修改,高层模块才能使用。
解决问题
1.进行抽象,提取公共方法getList();
/**
* @ClassName GetListDao
* @Description TODO
* @Author dsdj
* @Date 2018/10/9 下午8:14
* @Version 1.0
**/
public interface GetListDao {
void getList();
}
2.对每个实现,写一个实现类
实现获取用户的类
public class UserDao implements GetListDao {
public void getList() {
System.out.println("获取User的集合");
}
}
实现获取图书的类
public class BookDao implements GetListDao {
public void getList() {
System.out.println("获取book的集合");
}
}
3.重写SearchDao
package com.dsdj.design.principle.dependenceinversion;
/**
* @ClassName SearchDao
* @Description TODO
* @Author dsdj
* @Date 2018/10/9 下午7:46
* @Version 1.0
**/
public class SearchDao {
private GetListDao getListDao;
public SearchDao(GetListDao getListDao){
this.getListDao=getListDao;
}
public void setGetListDao(GetListDao getListDao) {
this.getListDao = getListDao;
}
public void searchlist(){
getListDao.getList();
}
}
Test
public class Test {
public static void main(String[] args) {
// SearchDao searchDao = new SearchDao();
// searchDao.getBooks();
// searchDao.getUsers();
// 重构之后的写法
SearchDao searchDao = new SearchDao(new BookDao());
// 获取图书
searchDao.searchlist();
// 获取用户
searchDao.setGetListDao(new UserDao());
searchDao.searchlist();
}
}
这样是不是就很有层次感了,
根据依赖倒置的原则:高层没有依赖底层的模块。例如SearchDao是高层模块,并没有依赖底层的UserDao、BookDao模块。有新需求的时候,只要再底层模块进行扩展。获取其他数据不会影响已经写好的模块。
相对于细节的多样性,抽象就稳定多了,故以抽象为基础搭建起来的架构就稳定多了。
现在我们再回头看看spring的依赖注入、控制反转,是不是有了新的理解了。代码不需要费心具体的实现类,把具体的实现类的权利交给容器负责就好了。
效果
我们回来再看之前的需求:新增一个获取课程的需求
我们可以怎么写呢?
写一个实现类CourseDao
public class CourseDao implements GetListDao {
public void getList() {
System.out.println("获取course集合");
}
}
Test调用
public class Test {
public static void main(String[] args) {
// SearchDao searchDao = new SearchDao();
// searchDao.getBooks();
// searchDao.getUsers();
// 重构之后的写法
SearchDao searchDao = new SearchDao(new BookDao());
// 获取图书
searchDao.searchlist();
// 获取用户
searchDao.setGetListDao(new UserDao());
searchDao.searchlist();
// 新增需求:获取课程
searchDao.setGetListDao(new CourseDao());
searchDao.searchlist();
}
}
运行结果
这样就可以不破坏原来的代码而实现新的功能了。
总结
- 对依赖倒置原则和开闭原则是相互联系的,在使用到依赖倒置原则过程中我们也不知不觉用到了开闭原则
- 对依赖倒置原则的学习,可以对一些框架的设计有新的理解