设计模式六大原则(3)——依赖倒置原则

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

         依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

         依赖倒置原则的核心思想是面向接口编程,我们依旧用一个例子来说明面向接口编程比相对于面向实现编程好在什么地方。场景是这样的,母亲给孩子讲故事,只要给她一本书,她就可以照着书给孩子讲故事了。代码如下:

  1.  1 class Book{  
     2     public String getContent(){  
     3         return "很久很久以前有一个阿拉伯的故事……";  
     4     }  
     5 }  
     6   
     7 class Mother{  
     8     public void narrate(Book book){  
     9         System.out.println("妈妈开始讲故事");  
    10         System.out.println(book.getContent());  
    11     }  
    12 }  
    13   
    14 public class Client{  
    15     public static void main(String[] args){  
    16         Mother mother = new Mother();  
    17         mother.narrate(new Book());  
    18     }  
    19 }  

     

运行结果:

妈妈开始讲故事
很久很久以前有一个阿拉伯的故事……

        运行良好,假如有一天,需求变成这样:不是给书而是给一份报纸,让这位母亲讲一下报纸上的故事,报纸的代码如下:

  1. 1 class Newspaper{  
    2     public String getContent(){  
    3         return "林书豪38+7领导尼克斯击败湖人……";  
    4     }  
    5 } 

     

     

        这位母亲却办不到,因为她居然不会读报纸上的故事,这太荒唐了,只是将书换成报纸,居然必须要修改Mother才能读。假如以后需求换成杂志呢?换成网页呢?还要不断地修改Mother,这显然不是好的设计。原因就是Mother与Book之间的耦合性太高了,必须降低他们之间的耦合度才行。

我们引入一个抽象的接口IReader。读物,只要是带字的都属于读物:

  1. 1 interface IReader{  
    2     public String getContent();  
    3 } 

     

     

Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,他们各自都去实现IReader接口,这样就符合依赖倒置原则了,代码修改为:

  1.  1 class Newspaper implements IReader {  
     2     public String getContent(){  
     3         return "林书豪17+9助尼克斯击败老鹰……";  
     4     }  
     5 }  
     6 class Book implements IReader{  
     7     public String getContent(){  
     8         return "很久很久以前有一个阿拉伯的故事……";  
     9     }  
    10 }  
    11   
    12 class Mother{  
    13     public void narrate(IReader reader){  
    14         System.out.println("妈妈开始讲故事");  
    15         System.out.println(reader.getContent());  
    16     }  
    17 }  
    18   
    19 public class Client{  
    20     public static void main(String[] args){  
    21         Mother mother = new Mother();  
    22         mother.narrate(new Book());  
    23         mother.narrate(new Newspaper());  
    24     }  
    25 }  

     

运行结果:

妈妈开始讲故事
很久很久以前有一个阿拉伯的故事……
妈妈开始讲故事
林书豪17+9助尼克斯击败老鹰……

    这样修改后,无论以后怎样扩展Client类,都不需要再修改Mother类了。这只是一个简单的例子,实际情况中,代表高层模块的Mother类将负责完成主要的业务逻辑,一旦需要对它进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。

    采用依赖倒置原则给多人并行开发带来了极大的便利,比如上例中,原本Mother类与Book类直接耦合时,Mother类必须等Book类编码完成后才可以进行编码,因为Mother类依赖于Book类。修改后的程序则可以同时开工,互不影响,因为Mother与Book类一点关系也没有。参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。现在很流行的TDD开发模式就是依赖倒置原则最成功的应用。

         传递依赖关系有三种方式,以上的例子中使用的方法是接口传递,另外还有两种传递方式:构造方法传递setter方法传递,相信用过Spring框架的,对依赖的传递方式一定不会陌生。
在实际编程中,我们一般需要做到如下3点:

  • 低层模块尽量都要有抽象类或接口,或者两者都有。
  • 变量的声明类型尽量是抽象类或接口。
  • 使用继承时遵循里氏替换原则。

        依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。

 

转自:http://blog.csdn.net/zhengzhb/article/details/7289269

转载于:https://www.cnblogs.com/zl1991/p/6283221.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/255583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习——决策树】——两种方法实现,含模型的保存和调用

目录 1、ID3算法 2、使用sklearn API——模型保存和调用成功 1、ID3算法 以下实现了决策树的创建、可视化绘制、决策树的保存和调用 但是在利用决策树进行预测的时候出现错误 分类代码 #实用决策树进行分类 def classify(inputTree, featLabels, testVec): firstStr = in…

重温 const 指针

在进行声明指针时,可以在类型前或后使用关键字const,也可在两个位置都使用。 下面都是合法的声明,但是含义大不同: const int * pOne; //指向整形常量 的指针,它指向的值不能修改 int * const pTwo; //指向整…

提交MTBF eservice以及log注意事项

[DESCRIPTION]提交MTBFeservice需要注意的描述,log事项[SOLUTION]提交MTBF eservice时,请注意1.描述清楚问题现象2. 描述清楚问题发生的时间点3.描述清楚问题发生时在run的case提交log时请注意1.MTBF的log通常会很大,若log太大,只…

Apache Cassandra和Apache Ignite:关系并置和分布式SQL

为什么80%的码农都做不了架构师?>>> 在上一篇文章中,回顾和总结了Cassandra中使用的查询驱动数据模型(或者说非常规数据模型)方法论的缺陷。事实证明,如果不对查询有深入的了解,通过该方法论将…

Android高级开发专题晋升班

Android高级开发专题晋升班 适用人群:1-3年以上经验的开发者丨学员平均薪酬20K/月转载于:https://www.cnblogs.com/lythonliu/p/6285531.html

使用opencv简单的播放AVI程序(40行)

学习OPENCV的第一个例子 #include <highgui.h> #include <cassert> #include <iostream> #include <Windows.h> using namespace std; void OnTrackbarSlide(int pos);int g_slider_position 0; CvCapture *g_capture NULL; int main(int argc , cha…

【纠错】——mysql Authentication plugin ‘caching_sha2_password‘ is not supported问题处理

mysql Authentication plugin ‘caching_sha2_password’ is not supported问题处理 使用mysql8.0版本&#xff0c;登录失败&#xff0c;提示 Authentication plugin ‘caching_sha2_password’ is not supported。 原因是在MySQL 8.0以后&#xff0c;默认的密码加密方式是cac…

关于EL表达式取值的问题

EL表达式取值时,如果没有指定作用域,EL表达式会自动按照作用域的大小,从小到大依次去找;比如${s},会自动按照"pageContext,request,session,application"的顺序去找属性名为s的属性.如果找到,则显示.否则,什么都不显示. 当Map中存整数时,如果想采用EL表达式取值,Map的…

统计信息自动收集任务失效原因排查

环境&#xff1a;Oracle 11.2.0.3 RAC问题&#xff1a;统计信息自动收集任务失效原因排查 1.查看自动任务的状态2.进一步查看其它信息3.解决问题1.查看自动任务的状态 查看自动任务的状态&#xff0c;确认是enabled状态&#xff1a; SQL> select client_name,status from db…

Markdown使用

#一级标题 ##二级标题 ###三级标题 斜体 粗体 斜体粗体 代码段> 删除内容效果是&#xff1a; 这是一级标题 这是二级标题 这是三级标题 这是斜体这是粗体这是斜体粗体 代码段 FileInputStream is new FileInputStream("text"); byte[] iput new byte[1024]; is.…

灰度图的width和widthstep的区别

灰度图的width是表示图像的每行像素数&#xff0c;widthstep指表示存储一行像素需要的字节数。 在OpenCV里边&#xff0c;widthStep必须是4的倍数&#xff0c;从而实现字节对齐&#xff0c;有利于提高运算速度。 如果8U单通道图像宽度为3&#xff0c;那么widthStep是4&#xff…

【pyradiomics学习】——安装pyradiomics以及简单示例

目录 数据集下载&#xff1a; 示例代码 参考文献&#xff1a; bug修复 运行结果&#xff1a; 数据集下载&#xff1a; https://www.jianguoyun.com/p/DcEwQq0Q45bOBxj09JYC (访问密码: gd8dmv) 示例代码 #!/usr/bin/env python # -*- coding: utf-8 -*- # Time : 20…

最新Django2.0.1在线教育零基础到上线教程(九)

演示地址&#xff1a; http://mxonline.mtianyan.cn 教程仓库地址1: https://github.com/mtianyan/DjangoGetStarted 教程仓库地址2: https://github.com/mtianyan/Mxonline2 教程仓库地址3: https://github.com/mtianyan/Mxonline3 9-1 讲师列表页 teacherlist 和 teacher det…

过滤器 拦截器 区别

转 http://www.cnblogs.com/wangyuyu/archive/2013/07/02/3167354.html1、拦截器是基于java的反射机制的&#xff0c;而过滤器是基于函数回调 2、过滤器依赖与servlet容器&#xff0c;而拦截器不依赖与servlet容器 3、拦截器只能对action请求起作用&#xff0c;而过滤器则可以对…

php --魔术常量 /魔术方法

魔术常量&#xff1a;1. __LINE__返回文件中的当前行号。2. __FILE__返回所在文件的完整路径。包含文件名3. __FUNCTION__返回所在函数名称。4. __CLASS__返回所在类的名称。5. __METHOD__返回所在类方法的名称。需要注意__METHOD__返回的是"class::function"的形式&…

【pyradiomics学习】——影像组学特征

目录 1、形状特征&#xff08;14个&#xff09; 2、一阶特征&#xff08;18个&#xff09; 灰度共生矩阵特征&#xff08;24个&#xff09; 灰度区域大小矩阵特征&#xff08;16个&#xff09; 灰度行程矩阵特征&#xff08;16个&#xff09; 邻域灰度差矩阵特…

NLP系列学习:前向算法和后向算法

在上一篇文章里,我们简单的概述了隐马尔科夫模型的简单定义在<CRF-tutorial>这一篇文章里,我们可以看到HMM经过发展之后是CRF产生的条件,因此我们需要学好隐马尔科夫模型.在这一部分,我比较推荐阅读宗成庆老师的<自然语言处理>这本书,这一部分宗老师写的很不错,相关…

Java日期处理 开始时间-结束时间查询

//开始时间 timeBegin " 00:00:00"; //结束时间 timeEnd " 23:59:59"; //时间转换 SimpleDateFormat format new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //Java时间类型转换成Long类型(可封装成工具类) public static Long stringToLong(St…

angular路由操作中'#'字符的解决办法

var appangular.module("myapp",["ngRoute"]);app.controller("ctr",function($scope){});//angular1.6.0以上版本需要配置app.config(["$locationProvider",function($locationProvider){ $locationProvider.hashPrefix(""…

【TypeError: float() argument must be a string or a number, not ‘map’】

初始 相关系数过滤法调用函数 from sklearn.feature_selection import SelectKBest from scipy.stats import pearsonr SelectKBest(lambda X,Y:np.array(map(lambda x:pearsonr(x,Y),X.T)).T,k2) .fit_transform(X_test,y_test) TypeError: float() argument must be a strin…