依赖倒置原则DIP(Dependence Inversion Principle)

依赖倒置原则的含义

  • 高层模块不能依赖低层模块,二者都应该依赖其抽象。
  • 抽象不应该依赖于细节。
  • 细节应该依赖抽象。

什么是高层模块?低层模块

每一个原子逻辑就是低层模块,原子逻辑再组就是高层模块。

什么是抽象和细节

抽象是抽象类,不可被实例化。

细节是实现类,比如实现的接口或继承抽象类的子类,可以被实例化。

表现在Java语言中就是面向接口编程

  • 模块间的依赖是通过抽象来实现的,具体的实现类之间不能发生直接的依赖。
  • 接口或抽象类不能依赖与实现类。
  • 实现类依赖接口或抽象类。

我们假设有三个类,一个为场景类,一个为司机类,一个为奔驰类。通过这三个类我们便可以实现司机开动汽车这个场景。如图



具体的实现代码如下

司机类

package des.DIP;
//司机类
public class Driver {
//司机驾驶车 紧耦合
public void drive(Benze benze){
benze.run();
}
//司机驾驶宝马车 紧耦合
public void drive(BMW bmw){
bmw.run();
}
}

奔驰类

package des.DIP;
//奔驰车
public class Benze {
public void run(){
System.out.print("奔驰车开始运行...");
}
}

场景类

package des.DIP;
//场景类
public class Client {
public static void main(String[] args){
//创建一个司机
Driver zs = new Driver();
//创建一个奔驰车
Benze benze = new Benze();
//司机可以开奔驰车
zs.drive(benze);
//假设此时增加一个宝马车呢?还要再增加一个方法,并且重新创建
//一个还好若是很多呢?难道要在司机类声明很多方法吗?
BMW bmw = new BMW(); } }
package des.DIP;
//宝马车
public class BMW {
//宝马车当然也可以开动
public void run(){
System.out.print("宝马车开动...");
}
}

程序正常的写法就是如此,但是如果我们考虑下面一个问题,司机并不是只会开着一辆Benze牌的车,假如我们再假如一个BMW(宝马)牌的车,我们传统的做法就是再新建一个类,然后再司机类中再添加一个drive BMW的方法。假如我们要添加无数品牌的汽车呢,难道还要再司机类中添加无数的drive方法吗?他们都有着相同的方法名,只是传入的汽车型号不同。

显然,传统的drive方法的写法,具有紧耦合性,只要车型变更,就不能再使用了。其导致的结果就是系统的可维护性大大降低,可读性也大大降低


解决方法

使用依赖倒置原则

DIP第一种方法 接口注入法

建立两个接口,IDriver和ICar



此时业务的场景类就可以改写成如下

package des.DIP;

public class Client1 {
public static void main(String[] args){
//创建一个司机
/**
* 此处明确两个概念:
* IDriver 叫做表面类型, Driver1 叫做实际类型 或称抽象类型和实际类型
*
* 此后所有的操作均是对抽象接口的操作,具体屏蔽了细节
*/
IDriver ds = new Driver1();
ICar c = new Bmw1();
ds.drive(c); }
}

表面类型和实际类型: IDriver 叫做表面类型, Driver1 叫做实际类型 或称抽象类型和实际类型

下面是接口类和实现类参考代码:

package des.DIP;
//司机接口
public interface IDriver {
//司机可以驾驶汽车,什么汽车不用管即抽象类(松耦合)
public void drive(ICar car);
}
package des.DIP;
//抽象汽车类
public interface ICar {
//汽车启动
public void run();
}
package des.DIP;

public class Driver1 implements  IDriver {
@Override
public void drive(ICar car) {
car.run();
}
}
package des.DIP;

public class Bmw1 implements  ICar {
@Override
public void run() {
System.out.print("宝马车开始运行...");
}
}
package des.DIP;

public class Benze1 implements  ICar {
@Override
public void run() {
System.out.print("奔驰车开始运行...");
}
}

假设我们项目中有两个类是依赖关系,此时我们只需要定义两个抽象类就可以独立开发了。

DIP第二种方法 构造函数传递依赖对象

package des.DIP;
//司机接口
public interface IDriver {
//司机可以驾驶汽车,什么汽车不用管即抽象类(松耦合)
public void drive(ICar car);
/***************************/
public void drive();
}
package des.DIP;

public class Driver1 implements  IDriver {
/******************************************************/
private ICar car;
//构造函数注入
public Driver1(ICar _car){
this.car = _car;
}
@Override
public void drive() {
this.car.run();
}
/******************************************************/
@Override
public void drive(ICar car) {
car.run();
} }
IDriver ds1 = new Driver1(new Bmw1());
ds.run();

运行结果



构造函数依赖注入理解图示





DIP第三种方法 setter方法传递依赖对象







代码参考

package des.DIP;
//司机接口
public interface IDriver {
public void setCar(ICar car);
public void drive(); }
package des.DIP;
public class Driver1 implements IDriver {
/******************************************************/
private ICar car;
@Override
public void setCar(ICar car) {
this.car.run();
} @Override
public void drive() {
this.car.run();
} }
package des.DIP;

public class Client1 {
public static void main(String[] args){ IDriver ds1 = new Driver1();
ds1.setCar(new Bmw1());
ds1.drive(); }
}

DIP总结

  • DIP本质就是通过抽象类来实现彼此独立,互不影响
  • 依赖倒置的核心是面向接口编程,即上面的第一种方法。
  • 依赖倒置的具体使用规则如下
    • 每个类尽量有接口或抽象类,或者二者都有。
    • 变量的表面类型尽量是接口或抽象类。
    • 任何类不应该从具体类派生。
    • 尽量不要覆写基类的方法。
    • 结合里氏替换原则进行。
  • 依赖倒置需要审时度势,而不是永远抓住这个原则不放,任何一个原则的优点都是有限的。

对于倒置的理解

从反面讲:什么是正置?如上例子,我们开什么型号的车,就依赖什么样型号的车。不存在什么抽象类与接口,直接单独建立即可,需要什么建立什么。但是依赖倒置?就是对车进行抽象,抽象出类和接口,建立抽象间的依赖。

六大设计原则(三)DIP依赖倒置原则的更多相关文章

  1. 《设计模式》-原则三:依赖倒置原则(DIP)

    这几天晚上回来都去玩了!没有坚持学习.真的好惭愧! 非常自责 后面一定要坚持 一气呵成  争取每天学一点,把这个学完. 今天主要是看了一下  设计模式中的 原则三: 依赖倒置原则(DIP) 官方是这样 ...

  2. C#软件设计——小话设计模式原则之:依赖倒置原则DIP

    前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...

  3. 【面向对象设计原则】之依赖倒置原则(DIP)

    依赖倒转原则(Dependency Inversion  Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象.换言之,要针对抽象(接口)编程,而不是针对实现细节编程. 开闭原则( ...

  4. 【设计模式六大原则3】依赖倒置原则(Dependence Inversion Principle)

      定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类 ...

  5. 面象对象设计原则之五:依赖倒置原则(The Dependency Inversion Principle,DIP)

    如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现.依赖倒转原则是Robert C. Martin在1996年为“C++Reporte ...

  6. DIP依赖倒置原则

    一.定义 1.高层模块不应该依赖低层模块,二者都应该依赖抽象 2.抽象不应该依赖于细节.细节应该依赖于抽象 二.层次化 1.简单介绍 结构良好的面向对象架构都具有清晰的层次定义,每个层次通过一个定义良 ...

  7. 九 DIP 依赖倒置原则

    首先看定义: 1.高层模块不依赖于低层模块,两者都应该依赖于抽象层 2.抽象不能依赖于细节,细节必须依赖于抽象 首先,模块是个抽象的概念,可以大到一个系统中的子系统作为一个模块,也可以是某个子系统中的 ...

  8. 第2章 面向对象的设计原则(SOLID):3_依赖倒置原则(DIP)

    3. 依赖倒置原则(Dependence Inversion Principle,DIP) 3.1 定义 (1)要依赖抽象,不要依赖具体的实现类.简单的说就是对抽象(或接口)进行编程,不要依赖实现进行 ...

  9. 依赖倒置原则(Dependency Inversion Principle)

    很多软件工程师都多少在处理 "Bad Design"时有一些痛苦的经历.如果发现这些 "Bad Design" 的始作俑者就是我们自己时,那感觉就更糟糕了.那么 ...

随机推荐

  1. LESS的简单介绍

    对于一些布局和样式比较复杂的网页,如何构建一个健康.优雅的CSS文件是一个很令人苦恼的问题.在书写静态页面的时候,我总是遇到布局结构累赘和重复样式复用性不高的问题,当然,对于这些问题归根究底还是要多联 ...

  2. laravel5.5 延时队列的使用

    队列这个知识相对比较冷门,因为平时的CURD基本用不到这个知识,今天用到了,所以就写个博客记录一下吧. 首先你得清楚要用什么驱动,除了database队列驱动(选择database驱动要php art ...

  3. Collection、List、Set集合概括

    1.Collection是一个接口,定义了集合相关的操作方法,其有两个子接口List和Set. 2.List和Set的区别    List是有序的可重复集合,Set是无序的不可重复集合. 3.集合持有 ...

  4. PAT1116: Come on! Let's C

    1116. Come on! Let's C (20) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue &quo ...

  5. 网络编程之套接字(udp)

    Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意思.通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机 ...

  6. SpringMVC接口测试异常:Can not deserialize instance of int out of START_OBJECT token

    之前使用springmvc搭建了restful风格的接口服务,在使用mockmvc进行集成测试的时候出现了异常:Can not deserialize instance of int out of S ...

  7. jQuery学习之旅 Item6 好用的each()

    1.javascript 函数的调用方式 首先来研究一下jquery的each()方法的源码,在这之前,先要回顾一下javascript函数具体调用样式: 普通函数调用 setName(); 可以作为 ...

  8. Android 打造任意层级树形控件 考验你的数据结构和设计

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40212367,本文出自:[张鸿洋的博客] 1.概述 大家在项目中或多或少的可能会 ...

  9. 关于new Date()的日期格式处理

    new Date()基本方法: var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整 ...

  10. java基础学习周计划之2--面向对象

    JAVA面向对象第一天一. 知识点:1. 类和对象二. 关键问题(理论):1. 简述什么是类.什么是对象2. 简述基本类型变量与引用类型变量赋值时的差别3. 简述null的含义三. 关键代码(操作): ...