依赖反转原则到底反转了什么
问题
SOLID是常用的用来设计类以及类和类之间关系的设计原则,它的主要作用就是告诉我们如何将数据和函数组织为类,以及如何将这些类链接为程序,从而使得软件容易扩展、容易理解以及容易复用。其中D代表的就是依赖反转原则(Dependence Inversion Principle),常见的解释就是高层模块和低层模块都应该依赖抽象,而不应该依赖具体实现,这样就能更好的实现模块间的解耦。但是反转又体现在了哪里呢?
面向对象编程
为了解决这种问题,先看一下面向对象编程,面向对象编程是常见的编程范式之一,同时还有结构化编程与函数式编程,这三种编程范式对程序员的编写模式做了限定,与具体的编程语言关系较小。有时候我们会说Java是面向对象的语言,而C是面向结构的语言,我理解这些是在语言设计之初,设计者就对于语言的语法做了相关范式的选择,但是用C能不能进行面向对象的编程呢?我想也是可以的,只要遵守相应的规范。比如说,面向对象编程的三个特点:封装、继承、多态通过C其实也可以实现。
安全稳定的多态
这里主要对比一下C语言的多态和Java的多态方式,C语言通过函数指针实现多态,Java通过接口或者父类的实现类来复写方法来实现多态。这里以实现屏幕的展示功能为例。
//C
//screen.h
struct Screen{
void (*show)();
}
//huaweiscreen.h
#include "screen.h"
void show(){ //具体实现}
struct Screen huaiWeiScreen={show};
//main.c
include "huaweiscreen.h"
int main(){
struct Screen* screen=&huaiWeiScreen;
screen->show();
return 0;
}
//Java
interface Screen{
void show();
}
class HuaWeiScreen implements Screen{
@Override
public void show(){
//具体实现
}
}
public static void main(String[] args){
Screen screen=new HuaiWeiScreen();
screen.show();
}
Java通过接口实现的多态在语义上同C语言通过函数指针实现多态的对比来看,要清晰的多,同时对比函数指针,也更为安全,毕竟如果函数指针如果指错了实现,那么整个程序就会造成难以跟踪和消除的bug。由此可以看出类似Java这种面向对象的语言为我们提供了一种更加容易理解、更加安全的的多态实现方式。
依赖反转
在安全和易于理解的多态支持出现之前,软件的架构是什么样子呢?
业务逻辑模块依赖数据库模块和UI模块,数据流动的方向和源码依赖的方向是一致的,但是这其实带来一个问题,业务逻辑本身其实并不关心数据库和UI的具体实现,使用MySQL还是SQLite以及是用文字展示UI还是动画展示UI业务逻辑不应该感知,但是因为缺乏一个容易理解、安全的多态方式,导致数据流动的方向和源码依赖方向必需保持一致,也让业务逻辑本身和数据库的具体实现以及UI的具体实现进行了绑定,这是不利于后续功能的扩展和修改的。 看一下使用多态后,架构设计的调整。
业务逻辑模块声明数据库接口类和UI接口类,数据库模块和UI模块通过依赖业务逻辑模块,实现对应的接口,并且在更高层模块注入业务逻辑,这样数据库模块和UI模块成为了业务逻辑的插件,这种插件式的架构方便了插件的替换,业务逻辑模块不再依赖数据库模块以及UI模块,可以进行独立的部署,数据库模块的修改和UI模块的修改也不会对业务逻辑造成影响。可以注意到数据流动的方向和源码依赖的方向不再保持一致了,这就是多态的好处,无论怎样的源代码级别的依赖关系,都可以将其反转。
结论
所谓依赖反转的反转,是指支持多态的语言相对于不支持多态的语言,数据流方向和源码依赖方向不用再保持一致,可以相反,源码依赖的方向完全由开发者控制
作者:滑板上的老砒霜
链接:https://juejin.cn/post/7147285415966277669
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。