六大设计原则(三)DIP依赖倒置原则

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

对于倒置的理解

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

原文地址:https://www.cnblogs.com/quinntian/p/10739188.html

时间: 2024-10-06 20:23:36

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

敏捷软件开发 – DIP 依赖倒置原则

DIP 依赖倒置原则 高层模块不应该依赖于低层模块.二者都应该依赖于抽象. 抽象不应该依赖于细节.细节应该依赖于抽象. 依赖于低层模块的高层模块意味着什么?正是高层模块包含了应用程序中重要的策略选择和业务模型.这些高层模块使得其所在的应用程序区别于其他.然而,如果这些高层模块依赖于低层模块,那么对于低层模块的改动会直接影响到高层模块,从而迫使它们依次做出改动.如果高层模块独立于低层模块,那么高层模块就可以非常容易地被重用.该原则是框架设计的核心原则. 层次化 糟糕的层次关系. 更为适合的模型.每

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

前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”,即各方便都有战斗力.关于设计模式,作为程序猿的我们肯定都不陌生.博主的理解,所谓设计模式就是前人总结下来的一些对于某些特定使用场景非常适用的优秀的设计思路,“前人栽树,后人乘凉”,作为后来者的我们就有福了,当我们遇到类似的应用场景的时候就可以直接使用了.关于设计模式的原则,博主将会在接下来的几篇里面

面向对象设计原则四:依赖倒置原则

依赖倒置原则(DIP)        定义:高层模块不应该依赖底层模块,两者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 好处:稳定性.可维护性.可扩展性. 概述:DI就是依赖倒置的意思,也可称为控制反转,我们以前编写结构化的程序当中,也就是C语言这样的语言时,高层模块依赖于底层模块,也就是调用者和被调用者的关系,调用者要依赖于被调用者,被调用者编写的一些功能和服务,会影响高层,一旦底层发生了变化,也就是被调用者发生了变化,就直接影响了高层也就是调用者.这样的设计,很难保证他的稳定性

设计模式六大原则之三:依赖倒置原则

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类A一般是高层模块,负责复杂的业务逻辑:类B和类C是低层模块,负责基本的原子操作:假如修改类A,会给程序带来不必要的风险. 解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率. 依赖倒置原则基于这样一个事实:相对于细节的多变性,

面象对象设计6大原则之五:依赖倒置原则

依赖倒置原则(DIP),The Dependency Inversion Principle 定义 1.高层模块不应该依赖低层模块,两都应该依赖于抽象. 2.抽象不依赖于具体细节. 3.具体细节应该依赖于抽象. 抽象就是指接口或者抽象类,细节是指实现接口或者抽象类的具体实现类. 也就是说模块之间的依赖通过接口或抽象发生的,两个实现细节之间不能直接发生依赖,接口不能依赖实现,实现应该依赖抽象. 我们在进行分布式系统开发时,比如常用的dubbo框架,各个系统的连接都是通过接口发生的,只要依赖对方的接

面向对象设计原则之四:依赖倒置原则

依赖倒置原则 所谓依赖倒置原则(Dependence Inversion Principle )就是要依赖于抽象,不要依赖于具体.简单的说就是对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合. 面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变化时,上层也要跟着变化,这就会导致模块的复用性降低而且大大提高了开发的成本. 面向对象的开发很好的解决了这个问题,一般的情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象.即使实现细节不断变化,只要抽象不变

[敏捷设计]5.DIP依赖倒置原则

一.定义 1.高层模块不应该依赖低层模块,二者都应该依赖抽象 2.抽象不应该依赖于细节.细节应该依赖于抽象 二.层次化 1.简单介绍 结构良好的面向对象架构都具有清晰的层次定义,每个层次通过一个定义良好的.受控的接口向外提供了一组内聚的服务. 对于这个陈述的简单理解可能会致使设计者设计出类似下图的结构. 图中,高层的Policy层使用了低层的Mechanism层,而Mechanism层又使用了更细节的Utility层.这样,Policy层对下面的Utility层的改动都是敏感的. 这种依赖关系是

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

3. 依赖倒置原则(Dependence Inversion Principle,DIP) 3.1 定义 (1)要依赖抽象,不要依赖具体的实现类.简单的说就是对抽象(或接口)进行编程,不要依赖实现进行编程,这样就降低了客户与实现模块间的耦合.包含3层含义: ①高层模块不应依赖低层模块,两者都应该依赖于抽象 ②抽象不应该依赖细节 ③细节应该依赖于抽象 (2)何为“高层模块”和“低层模块” ①“低层模块”:每个逻辑的实现都是原子逻辑组成,不可分割的原子逻辑就是低层模块.一般和具体实现相关. ②“高层

C#编程:依赖倒置原则DIP

一.前言 我们先来看看传统的三层架构,如下图所示: 从上图中我们可以看到:在传统的三层架构中,层与层之间是相互依赖的,UI层依赖于BLL层,BLL层依赖于DAL层.分层的目的是为了实现“高内聚.低耦合”.传统的三层架构只有高内聚没有低耦合,层与层之间是一种强依赖的关系,这也是传统三层架构的一种缺点.这种自上而下的依赖关系会导致级联修改,如果低层发生变化,可能上面所有的层都需要去修改,而且这种传统的三层架构也很难实现团队的协同开发,因为上层功能取决于下层功能的实现,下面功能如果没有开发完成,则上层