谈[SOLID设计原则]依赖倒置原则

2,154 阅读5分钟

前言

在计算机世界摸爬滚打多年以后,前辈们发现软件系统总是在不断迭代中,产生了坏味道,让后续的系统变更举步维艰。为了应对呢,他们总结出了几条软件设计的经验,后来被称作SOLID原则:

  • Single Responsibility Principle(单一职责原则)
  • Open Closed Principle(开闭原则)
  • Liskov Substitution Principle(里氏替换原则)
  • Interface Segregation Principle(接口隔离原则)
  • Dependency Inversion Principle(依赖倒置原则)

依赖倒置原则

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

这个原则的实现方法比较简单,本文不做赘述了,主要还是想说一下个人的理解,我认为可以从两个角度进行讨论:分层和抽象。

高层模块不应该依赖低层模块

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

单一职责原则里,我们将职责相同的代码组织到同一个地方,不同职责的代码和模块,构成了不一样的分层。我们以最简单的架构举例:负责处理请求和响应的,归为接口层;处理核心业务逻辑的,叫业务层;处理数据库增删改查的,叫仓库层

如果按照直觉,或者说数据流向来设计层级之间的依赖,将会是这样:接口层->业务层->仓库层。

不是说这种做法有多大问题,只是它的可修改性会有点差,面临某些变更时可能会难以应对。

请看例子:

假设你在电商业务,商家管理后台中,可以查看商品列表。在项目初期,业务量还很小,直接使用MySQL进行查询。

直觉设计是,仓库层提供一个Query方法,业务层代码直接调用:

graph LR
biz --> A["mysql.Query(params4DB)"]

后来业务迅猛发展,入驻的商家日益增多,后台的请求变量大了;各个商家创建的商品越来越多,寻找商品效率太低了,MySQL的like查询,一次就能把数据库整死。此时你的团队判断,需要借助ES的模糊查询才能更好的支持了。于是需要在仓库层提供一个es的Search方法,并在业务层把之前的查询方法替换一波,之前MySQL查询所需要的参数、返回值的类型,也需要相应修改

graph LR
biz --> A["es.Search(params4ES)"]

业务没变,却要业务层进行修改,凭啥呢?

高层和低层

软件系统就是为业务服务的,其中的业务逻辑就是一个软件系统的核心价值,而这些重要的业务逻辑都应该收敛在业务层。相对地,接口层只负责将这些能力暴露给外部,仓库层只负责支撑业务数据的读写,选择使用MySQL还是ES,只是一种实现细节,不会改变产品形态。

所以这里的业务层妥妥的就属于高层,地位最高,是系统的核心,接口层仓库层属于底层,地位较低,为业务层服务,是系统的实现细节。

现在我们有了高层和低层的概念了,按依赖倒置的做法,上面的例子就可以改进为:

graph LR
biz -.调用.-> A["repository.Search(params4Search)"]


repository_impl -.实现.-> A

mysql --> repository_impl
es --> repository_impl

业务层:bizrepository.Search(params4Search)

仓库层:repository_implmysqles

其中repository.Search(params4Search)是抽象接口,Search方法的参数和返回值格式,也都是业务层定义的,仓库层按照抽象接口的定义去实现。此时依赖关系就变为:高层没有依赖低层,低层依赖了高层定义的抽象接口。

日后支持高并发,需要借助Redis,就直接在仓库层修改就行了,业务层完全不用做修改的喔

细节应该依赖抽象

抽象不应该依赖细节,细节应该依赖抽象

对于后半句,我觉得只说出了一种指引或是实现方式。但我们更应该知道的是为什么要这样。

能够被抽象出来的模式,总是经过反复的推导、修改、验证的,能够通用、稳定地表达某一种业务形态的。只要业务符合该模式,就可以一直复用。

细节则是指具体,一个具体的对象和另一个具体的对象,是有区别的,不可复用的。一般来说,也是多变(经常做修改)的。

所以后半句话的重点应当是:一个稳定(少变)的模块,不应该依赖一个不稳定(多变)的模块。 类似于木桶效应。系统是一个整体,可修改性总会取决于最不可修改的一个模块(针对开发时,非运行时)。

而依赖倒置,才是实现手段。当你发现:核心模块依赖了一些具体实现,具体实现有改动时,连带着核心模块也要做相应修改,这时候可以通过依赖导致的方式,去让核心模块依赖一层抽象定义,让其变得更稳定。

总结

依赖这个词在现实生活的含义是:依靠别人或事物而不能自立或自给。A是依赖B的,A就失去了主动,B对于A来说不可或缺,B有变动A就需要跟随。

依赖倒置原则的思想是,确定好高层和底层,让底层去依赖高层(定义的抽象接口),而不是反过来。