设计模式之----依赖倒置(Dependency inversion principle)的理解

1.前言

依赖倒置是面向对象设计领域的一种软件设计原则。 有人会有疑惑,设计原则有什么用呢?设计原则是前辈们总结出来的经验,你可以把它们看作是内功心法。只要你在平常开发中按照设计原则进行编码,假以时日,你编程的功力将会大增。

2.概念

依赖倒置原则的原始定义为:上层模块不应该依赖下层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,其核心思想是:要面向接口编程,不要面向实现编程。这个是官方的定义,有些人一看可能是懵的!我们该怎么理解上面的定义呢?我们需要咬文嚼字,各个突破。


2.1 什么是依赖?

依赖是一种关系,通俗来讲就是一种需要,人钓鱼?人需要鱼竿钓鱼,因为人就没有鱼竿就钓不了鱼,,所以说人依赖鱼竿,鱼竿被人依赖。在面向对象编程中,代码可以这样编写。

class  fishing{
    private fishPole:fishPole;
    public fishing(){
        this.fishPole = new fishPole();
    }
}

fishing 的内部持有 fishPole 的引用,这就是依赖在编程世界中的体现。

2.2 什么是上层模块和下层模块?

俗话说,人人平等,但是为什么在同一个公司,你的工资就比你的上级领导工资要少呢?上级领导又比CEO要少呢?(对领导和你来说,那么上级领导就是上层,你就是下层,对于领导和CEO来说,CEO就是上层,领导就是下层),对于任何一个组织机构来说,它是有职能的划分的。按照职能的重要性,自然而然就有了上下之分。模块也是一样,也许某一模块相对于另外一模块它是下层,但是相对于其他模块它又可能是上层。

2.3 什么是抽象和细节?

抽象如其名字一样,是一件很抽象的事物。抽象往往是相对于具体而言的,具体也可以被称为细节,比如: 汽车就是抽象,而宝马520,奔驰E200,就是具体了,抽象可以是物是人也可以是行为!

3.作用

1. 可以降低类间的耦合性。
2. 可以提高系统的稳定性。
3. 可以减少并行开发引起的风险。
4. 可以提高代码的可读性和可维护性。

4.实现方法

使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成。所以我们在实际编程中只要遵循以下4点,就能在项目中满足这个规则。

1. 每个类尽量提供接口或抽象类,或者两者都具备。
2. 变量的声明类型尽量是接口或者是抽象类。
3. 任何类都不应该从具体类派生。
4. 使用继承时尽量遵循里氏替换原则。

5.实例

一个非常简单的例子,小明目前是骑车去上学,此例子只是反映小明和车之间的依赖关系!

你可能会这么画类图

在这里插入图片描述

//Xiaoming.ts
import { Bike } from "./Bike"

export class xiaoMing {
    private bike: Bike
    public goSchool() {
        this.bike = new Bike();
        this.bike.drive();
    }
}

//Bike.ts
export class Bike{
    public drive(){
        console.log("bike drive")
    }
}

某天,天空下雨了,小明不能骑自行车了,要做公交车了,然后你改代码

//Xiaoming.ts
import { Bike } from "./Bike"
import { Bus } from "./bus";
export class xiaoMing {
    private bike: Bike
    private bus: Bus
    public goSchool() {
        // this.bike = new Bike();
        this.bus = new Bus();
        this.bus.drive();
    }
}

又某天,小明要迟到了赶时间,小明要打的去学校,然后你改代码

//Xiaoming.ts
import { Bike } from "./Bike"
import { Bus } from "./Bus";
import { Taxi } from "./Taxi";

export class xiaoMing {
    private bike: Bike;
    private bus: Bus;
    private taxi: Taxi;
    public goSchool() {
        // this.bike = new Bike();
        // this.bus = new Bus();
        this.taxi = new Taxi();
        this.taxi.drive();
    }
}

类图就变成了这样
在这里插入图片描述

这样每次添加一个类,你都要改代码,有没有一种方法能让 xiaoMing 的变动少一点呢?因为这是最基础的演示代码,如果工程大了,代码复杂了,xiaoMing 面对需求变动时改动的地方会更多。而依赖倒置原则正好适用于解决这类情况。先看下类图

在这里插入图片描述

我们的代码中,xiaoMing是个具体类,所以它需要一个Person接口, xiaoMing没有依赖抽象,所以我们得引进抽象。而底层的抽象是什么,是 Driveable 这个接口。


//Person.ts
export interface Person {
    goSchool();
}

//Driveable.ts
export interface Driveable {
    drive();
}

//Bike.ts
import { Driveable } from "./Driveable";

export class Bike implements Driveable{
    public drive(){
        console.log("bike drive")
    }
}

//Bus.ts
import { Driveable } from "./Driveable";

export class Bus implements Driveable{
    public drive(){
        console.log("bus drive")
    }
}

//Taxi.ts
import { Driveable } from "./Driveable";

export class Taxi implements Driveable{
    public drive(){
        console.log("taxi drive")
    }
}

//xiaoMing.ts
import { Driveable } from "./Driveable";
import { Person } from "./Person";

export class xiaoMing implements Person {
    private drive;
    constructor(drive: Driveable) {
        this.drive = drive;
    }
    public goSchool() {
        this.drive.drive();
    }
}

//外层调用
let xiaoM = new xiaoMing(new Bus());
xiaoM.goSchool();

xiaoMing类中 drive() 这个方法依赖于 Driveable 接口的抽象,它没有限定自己出行的可能性,任何 Car、Bike 或者是 Bus都可以的。我们可以说是符合了上层不依赖于底层,依赖于抽象的准则了。那么,抽象不应该依赖于细节,细节应该依赖于抽象又是什么意思呢?Driveable 是抽象,它代表一种行为,而 Bike、Car、Bus都是实现细节。如果我改变了实现细节里面的逻辑代码是不会影响到Driveable 抽象,然后我改了Driveable抽象必然会影响到细节的实现,所以说抽象不应该依赖于细节,细节应该依赖于抽象!关系变化如图:
在这里插入图片描述在这里插入图片描述

不难发现,上面的依赖箭头发生了改变。所以依赖倒置也由此而来,上层模块xiaoMing不应该依赖下层bike模块,它们都应该依赖于抽象Driveable。依赖倒置我们可以理解为依赖关系被改变,倒置的其实是下层细节,原本它是被上层依赖,现在它倒要依赖与抽象的接口,所以说依赖倒置实质上是面向接口编程的体现。

  • 18
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
SOLID原则中的依赖倒置原则Dependency Inversion Principle,DIP)是指高层模块不应该依赖底层模块,二者都应该依赖于抽象接口;抽象接口不应该依赖于具体实现,而具体实现应该依赖于抽象接口。 简单来说,DIP原则就是通过接口来解耦高层模块和底层模块之间的依赖关系,使得系统更加灵活、可维护、可扩展。在设计和开发过程中,我们应该遵循DIP原则,尽可能使用接口或抽象类来定义模块之间的依赖关系,而不是直接依赖具体实现类。 举个例子,假设我们正在开发一个电商系统。我们有一个OrderService类,它依赖于一个底层模块的OrderDao类来实现订单数据的持久化。如果我们直接在OrderService类中实例化OrderDao对象,那么OrderService类就与OrderDao类紧密耦合,如果我们需要更换一种不同的数据持久化方案,那么就需要修改OrderService类的代码,违反了开闭原则(Open Close Principle,OCP)。 为了遵循DIP原则,我们可以先定义一个抽象的OrderDao接口,然后让OrderService类依赖于OrderDao接口。底层模块的具体实现类可以实现OrderDao接口,这样就可以实现数据持久化的功能,同时也可以轻松地更换不同的数据持久化方案,不需要修改OrderService类的代码。 总之,DIP原则是设计模式中非常重要的原则之一,它可以帮助我们构建更加灵活、可维护、可扩展的系统。在实际开发中,我们应该尽可能地遵循DIP原则,使用接口或抽象类来定义模块之间的依赖关系,降低模块之间的耦合度,提高系统的可维护性和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值