书接上回,本篇继续讲一下设计模式六大原则(有些书认为是7大原则)

原则定义

1>高层次的模块不要依赖低层次的模块,二者都应该依赖于其抽象

2>抽象不应该依赖于具体,而是具体应该依赖于抽象

看懂上面的定义得提前理解几个词:高层次模块,低层次模块, 抽象,具体

高层次模块:也叫上层代码,一般可以认为调用方。以系统三层结构(表现层,业务层,持久层)为例子,表现层相对于业务层来说,就是高层,业务层相对于持久层来说就是低层。

低层次模块:也叫为下层,一般可以认为被调用方。以系统三层结构(表现层,业务层,持久层)为例子,业务层相对于表现层来说,就是低层,持久层相对于业务层来说就是低层。

抽象:设计模式的中的抽象可以理解为约束/规范,代码中表现就是:接口/抽象类

具体:设计模式中的具体就是抽象具现化,也即:接口实现类/抽象类的子类

上面总结一句话就是:面向接口编程,再精简一点就是:面向多态编程

案例分析

需求:实现多终端(pc端/app端)签到案例

/*** App端签到*/
public class AppClient {public void signIn(){System.out.println("app端签到.....");}
}
/*** pc端签到*/
public class PCClient {public void signIn(){System.out.println("pc端签到.....");}
}
/*** 签到用户*/
public class User {//pcpublic void signInPC(PCClient client){client.signIn();}//apppublic void signInApp(AppClient client){client.signIn();}
}

测试:

public class App {public static void main(String[] args) {//签到用户User user = new User();user.signInPC(new PCClient());   //pc签到user.signInApp(new AppClient());  //app签到}
}
结果:
pc端签到.....
app端签到......

解析

上面案例中App是高层次类,User是低层次类,

App,User, AppClient PCClient 都是具体,没有抽象

App VS User 

App是高层次类, User为低层次类

User VS  AppClient/PCClient

User是高层次类, AppClient/PCClient是低层次类

当前存在的问题:

1>App类需要调用User类签到方法实现不同签到,如果User类缺少某些签到方法,App类也缺少某些签到方法。此时的App类严重依赖User类。这不符合依赖倒置原则中高层次模块不要依赖低层次模块

2>User类是具体类,需求变动时(比如:添加了小程序签到),User类就必须额外添加小程序签到方法。User类就必须变动了。这就违反了开闭原则【对拓展开发,对修改关闭】。

改版:

将签到逻辑抽象成行为,让User类依赖不再是具体的AppClient/PCClient而是抽象接口

/*** 签到行为(行为约束/规则)*/
public interface ISign {void signIn();
}
/*** App端签到*/
public class AppClient implements ISign{public void signIn(){System.out.println("app端签到.....");}
}
/*** pc端签到*/
public class PCClient implements ISign{public void signIn(){System.out.println("pc端签到.....");}
}
/*** 签到用户*/
public class User {//pc/apppublic void signIn(ISign client){client.signIn();}
}

测试:

public class App {public static void main(String[] args) {//签到用户User user = new User();ISign sginPC = new  PCClient();ISign sginApp = new  AppClient();user.signIn(sginPC);   //pc签到user.signIn(sginApp);  //app签到}
}

解析:

1>改版后的App类对User类依赖降低了,App类需要什么签到功能只需要new出不同的签到客户端对象即可。User类只需要提供固定签到方法即可。这符合高层次的模块不要依赖低层次的模块

另外User类对 AppClient/PCClient类的依赖由原先的具体类转成抽象接口,结构更稳啦,因为接口定制规则已经固定了,具体类再怎么变化都在规则之内。

2>如果需求变化,需要加入新的签到功能,只需按照ISign接口约定签到规则创建新的签到客户端类即可。User类完全不需要变动。因为:具体(User类)依赖于抽象(ISign类),接口的稳定性高于具体实现类。

思考

 这样设计意义在哪?

1> 系统稳定性,维护性,拓展性考虑

一个好的系统架构必须是一个稳定系统架构,设计系统架构时遵守依赖倒置原则是必须的,这怎么理解?回到传统版的代码设计,如果User类需要涉及到各种终端的对接,如果单纯去添加User类的signInXx方法,势必引入不可以预知的变动。比如:如果User有子类,子类乱重写,现有的终端类失效(甚至要去除)。

传统版中User类对接各种终端具体实例,这种设计在系统后期维护与拓展上也会带来挑战。原因:各种终端实例对签到这个逻辑没有具体约定,实现完全是凭程序员个人修养。比如: 客户端签到方法设计为 signPC signApp signXxx等等。那怎么解决这些乱象,设计出优雅的架构呢?很简单:遵循依赖倒置原则,制定好规范,按照规范设计代码。

具体实现是多变的,而抽象是稳定,以抽象为基础搭建起来的架构自然比以具体实现为基础搭建起来的架构要稳定的多。

2>减少耦合

改版后的设计,可以发现,高层次模块(User)对低层次模块(PCClient、AppClient)调用依赖中间的ISign接口,使用接口隔开高低层的直接接触,高层次模块只需要按实现约定的规则(ISgin)调用,不需要关注低层次模块的具体实现, 低层次模块按照约定规则实现与传值即可。这减少2个层面的代码存在过多耦合,有利于后续系统拓展与优化。

实现方式

开发中依赖倒置原则实现方式常见有3种:

1>接口传递

/*** 签到用户*/
public class User {//pc/apppublic void signIn(ISign client){client.signIn();}
}

2>构造器方式

/*** 签到用户*/
public class User2 {private ISign sign;public User2(ISign sign){this.sign = sign;}//pc/apppublic void signIn(){sign.signIn();}
}

3>setter方式

/*** 签到用户*/
public class User3 {private ISign sign;public void setSign(ISign sign){this.sign = sign;}//pc/apppublic void signIn(){sign.signIn();}
}

运用

我们还是从JDK里面找例子,集合中的排序方法:

Comparator 接口规定了排序的规则,两两比较,由最终返回值决定谁大谁小,谁先谁后。

@FunctionalInterface
public interface Comparator<T> {int compare(T o1, T o2);
}

2个排序规则:

//正排, 匿名内部类
Comparator<Integer> cp1 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1 - o2;}
};
//倒排,匿名内部类
Comparator<Integer> cp2 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}
};

比较:

public class App {public static void main(String[] args) {List<Integer>  list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);//正排Comparator<Integer> cp1 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1 - o2;}};//倒排Comparator<Integer> cp2 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}};list.sort(cp1);System.out.println(list); //[1,2,3,4]list.sort(cp2);System.out.println(list);//[4,3,2,1]}
}

解析

Comparator 接口定义规定中,高层模块(list集合sort方法)按照规则调用底层模块(Comparator 接口正排倒排实现)。

总结

依赖倒置原则使用过程中要明确的:

1>低层模块尽量依赖有抽象类或接口, 也即低层模块代码需要按规范实现,不能天马行空。

2>高层模块依赖低层模块的抽象规范(接口)而不是具体实现,有利于程序扩展和优化

一句话概括: 面向接口编程~

浅谈设计模式-依赖倒转(倒置)原则相关推荐

  1. 设计模式原则之依赖倒转(倒置)原则

    设计模式原则之依赖倒转(倒置)原则 原则概述 依赖倒转原则(Dependence Inversion Principle)是指: 高层模块不应该依赖底层模块,二者都应该依赖其(或接口)抽象(不要依赖具 ...

  2. 六大原则之依赖倒转(倒置)原则

    1. 依赖倒转(倒置)原则 介绍 高层模块不应该依赖低层模块,二者都应该依赖其抽象 抽象不应该依赖细节,细节应该依赖抽象 依赖倒转(倒置)的中心思想是面向接口编程 依赖倒转原则是基于这样的设计理念:相 ...

  3. Java设计模式七大原则-依赖倒转(倒置)原则

    目录 依赖倒转原则 依赖关系传递的三种方式和应用案例 1. 接口传递 2.构造方法传递 3.setter方式传递 依赖倒转原则的注意事项和细节 依赖倒转原则 基本介绍 依赖倒转原则(Dependenc ...

  4. Java设计模式 - 依赖倒转原则

    文章目录 1. 违反依赖倒转原则 2. 遵守依赖倒转原则 3. 依赖关系传递的3种方式 3.1 接口传递 3.2 构造方法传递 3.3 setter方法传递 4. 依赖倒转原则的注意事项和细节 Dep ...

  5. 设计模式常用的七大原则之③【依赖倒转】原则

    文章目录 概念 案例 代码1.0 解决思路 依赖关系传递的三种方式和应用案例 接口传递 构造方法传递 set传递 依赖倒转原则的注意事项和细节 概念 依赖倒转原则(Dependence Inversi ...

  6. 23种设计模式-依赖倒转原则

    概念: 依赖倒转原则(Dependence Inversion Principle)是指: 1) 高层模块不应该依赖低层模块,二者都应该依赖其抽象 2) 抽象不应该依赖细节,细节应该依赖抽象 3) 依 ...

  7. php 如何设计索引_Mysql学习浅谈mysql的索引设计原则以及常见索引的区别

    <Mysql学习浅谈mysql的索引设计原则以及常见索引的区别>要点: 本文介绍了Mysql学习浅谈mysql的索引设计原则以及常见索引的区别,希望对您有用.如果有疑问,可以联系我们. 索 ...

  8. Java设计模式——依赖倒转原则

    一.什么是依赖倒转原则? 依赖倒转原则讲的是,要依赖于抽象,不要依赖于具体. 实现"开-闭"原则的关键是抽象化,并且从抽象化导出具体化实现."开-闭"原则是面向 ...

  9. unity 项目开发——浅谈设计模式的六大原则(一)

    目录 前言 首先,六大原则是谁? 其次,为什么需要学习这六大原则? 正文 一.单一职责原则 示例: 因此我们需要进行拆分,根据具体的职能可将其具体拆分如下: Unity 单一职责原则 二.开闭原则 U ...

最新文章

  1. 使用google云(GCP)二次利用安装kali Linux(kali browser)
  2. 高效使用Vector
  3. jQuery中position()与offset()区别
  4. 使用Spring Security保护GWT应用程序
  5. QT 5 1 0 MinGW 的安装及使用
  6. 【论文笔记】一种有效攻击BERT等模型的方法
  7. 创建此对象的程序是quation_MathType出现此对象创建于Equation中的问题怎么办
  8. 10-11-根据文章标题搜索文章
  9. CentOS误删python环境后怎么办?
  10. Atitit.数据库存储引擎的原理与attilax 总结
  11. ppt学习02——字体
  12. postman构造post请求用于接口测试
  13. python实现ks算法_Python计算KS值并绘制KS曲线
  14. 18100出多少取整_电子表格里小数取整用什么公式?
  15. 华为OD机试 - 日志首次上报最多积分
  16. I2C、Arduino、ADXL345、
  17. 【Flutter】mounted
  18. FFmpeg引入x264扩展
  19. xp系统服务器管理器怎么打开方式,winXP系统怎么打开控制面板管理工具
  20. 单向链表与双向循环链表

热门文章

  1. video视频相关问题:火狐浏览器报错“没有找到支持的视频格式和MIME类型”
  2. 软件众包网站有哪些?
  3. 暗影精灵7安装Ubuntu双系统、RTX3060 Nvidia 驱动及搭建深度学习环境
  4. 华硕FL5900U笔记本电脑重装win10专业版详细操作教程
  5. 前端用 js-file-download组件 下载后端返回的Excel文件
  6. 极限思想在计算机中的应用,高等数学中极限思想的应用
  7. AutoCAD2024最新版介绍及安装下载
  8. 数值计算中的overflow and underflow
  9. 如何成为一名优秀的技术Leader?
  10. 10款 好用的php 编辑器