设计模式|设计原则之依赖倒置原则

设计模式|设计原则之依赖倒置原则

记录对设计模式学习的过程

前言

  • 定义:高层模块不应以依赖低层模块,二者都应依赖其抽象

  • 抽象不应该依赖细节;细节应该依赖抽象

  • 针对接口编程,不要针对实现编程

  • 优点:低耦合 、、、、等等

详细概念可以网上查询

案例实验

有一个查询数据的类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 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(); } } 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(); } /** * @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 UserDao implements GetListDao { public void getList() { System.out.println("获取User的集合"); } }

实现获取图书的类

public class BookDao implements GetListDao { public void getList() { System.out.println("获取book的集合"); } } 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(); } } 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(); } } 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集合"); } } 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(); } } 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(); } }

在这里插入图片描述

运行结果
在这里插入图片描述

这样就可以不破坏原来的代码而实现新的功能了。

总结

  • 对依赖倒置原则和开闭原则是相互联系的,在使用到依赖倒置原则过程中我们也不知不觉用到了开闭原则
  • 对依赖倒置原则的学习,可以对一些框架的设计有新的理解