设计原则 - 依赖倒置原则(DIP)

依赖倒置原则

依赖倒置原则(Dependence Inversion Principle, DIP)原始定义是

High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.

翻译过来就是:

1、高层模块不应该依赖低层模块,两者都应该依赖其抽象

2、抽象不应该依赖细节

3、细节应该依赖抽象

高层模块:通常只策略、业务场景

低层模块:也就是具体实现的细节

抽象:抽象就是指接口或抽象类,两者都是不能直接被实例化的

细节:就是实现类,实现接口或继承抽象类而产生的类就是细节

 

通俗一点:依赖倒置就是通过抽象(接口或抽象类)使各个类或者模块的实现彼此独立,互不影响,实现模块间的松耦合。

依赖倒置原则在编码中经常被使用,其核心思想就是面向接口编程,而不是面向实现编程。接口是指定义(规范、约束)与实现相分离,它是一种抽象,只要不修改接口声明,那么就可以放心使用,至于接口内部的实现无需关心。所以面向接口编程也可以简单理解为面向协议编程,实现者按照协议来工作。理解了面向接口编程也就理解了依赖倒置。

 

问题描述

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

 

解决方案

将类A修改为依赖接口P,类B和类C都各自实现接口P,类A通过接口P间接与类B和类C发生联系,这样能够大大降低修改类A的几率。

 

Demo

师傅开车,我们有一个司机类,司机目前可以开奔驰车,所以我们声明类和方法如下:

// 司机
class Driver {
    func drive(_ benZ: BenZ) {
        benZ.run()
    }
}

// 奔驰
class BenZ {
    func run() {
       print("benZ start to run...")
    }
}

代码非常简单,司机开奔驰车那是开的非常开心,但是这时候司机又想开宝马了,或者司机想要开奥迪了,这该怎么办呢?目前我们的Driver类中的驾驶方法与BenZ类那是紧密耦合啊,司机想开其他车还真难,虽然我们可以为Driver类在添加新的方法来让司机开宝马,但是这样其实是不友好的,重复代码太多,明明就是开车的功能,写这么多方法,显然不对,为了解决这个问题,我们必须去除Driver类对奔驰类的依赖,那么就该使用DIP原则

1、声明汽车协议

// 声明汽车协议
protocol CarProtocol {
    func run()
}

2、让奔驰、宝马都遵循开汽车协议

// 奔驰
class BenZ: CarProtocol {
    func run() {
       print("benZ start to run...")
    }
}

// 宝马
class BMW: CarProtocol {
    func run() {
        print("bmw start to run...")
    }
}

3、司机只需要暴露驾驶方法即可,依赖协议进行开车

// 司机
class Driver {
    func drive(_ car: CarProtocol) {
        car.run()
    }
}

 现在司机想开什么车子就开什么车子,如果想开奥迪,创建一个奥迪类,遵守开汽车协议,那么就OK了

let driver = Driver()
// 开奔驰
driver.drive(BenZ())
// 开宝马
driver.drive(BMW())

如果想进一步优化,其实driver方法也应该声明为接口,即协议方法,但是个人感觉目前其实OK了,符合我们所讲的高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象

 

依赖倒置原则优点

1、可以减少类间的耦合性

2、提高系统的稳定性

3、降低并行开发引起的风险

4、提高代码的可读性和可维护性

 

依赖注入

依赖是可以传递的,A对象依赖B对象,B对象又依赖C对象,C对象又依赖D。。。生生不息,依赖不止,但是记住一点:只要做到抽象依赖,即使是多层的依赖传递也是无所谓的。

依赖注入的方式,更多详细细节看这里

1、构造函数注入

class Driver {
    let car: CarProtocol

    init(_ car: CarProtocol) {
        self.car = car
    }

    func drive() {
        car.run()
    }
}

2、属性注入

class Driver {
    var car: CarProtocol?
    func drive() {
        car?.run()
    }
}

3、方法注入

class Driver {
    func drive(_ car: CarProtocol) {
        car.run()
    }
}

 

 

参考

快速理解 设计模式六大原则

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

 

 

你可能感兴趣的:(iOS,设计模式)