依赖倒置原则

发布于: 2020 年 06 月 17 日
依赖倒置原则

什么是依赖倒置

依赖倒置:Dependence Inversion Principle

我们先来看看Inversion的英文释义:

the action of inverting something or the state of being inverted.

(invert : put upside down or in the opposite position, order, or arrangement. )

如果有兴趣再搜索下Inversion图片多半会看到这样一张图片

图片直观的表现了一种上下颠倒的状态

在OOD中,依赖倒置指高层类的开发,不依赖低层类的实现细节,而是依赖于抽象接口。

当构建一个系统的时候,我们会在一些比较底层的类中实现一些基本的功能,比如记日志的类,操作数据库的类。在高级点的类中,去完成一些复杂逻辑(比如业务流程)。实现时,一种比较自然的做法就是,我们先写低层类,然后在高层类中调用底层的类完成复杂功能,也就是说高层依赖低层。但是这不算一种灵活的设计,比如如果低层的实现发生了改变时,高层不得不跟着变化修改。

举例说明

我们具体举例来说。我们有个数据库操作类DBExecutor,能够实现Create,Update,Delete三个方法,经数据更新到数据库。

public class DBExecutor{
public void Create<T>(T data)
{
//......
}
public void Update<T>(T data)
{
//......
}
public void Delete<T>(T id)
{
//......
}
}

接着我们在所有的应用层类中实现业务,将数据存储到数据库,处处都是new DBExecutor(),然后调用Create,Update,Delete的操作。比如

public class OrderAppService
{
//生成订单
public void CreateOrder(Order order,DBExecutor db)
{
//......前略......
db.Insert(order);
}
}

一开始我们的数据库使用的是SQL Server数据库,一切正常,突然有一天,领导决定我们要改用NoSQL型数据库存储。那我们首先会再实现一个NoSQLDBExecutor,这部分比较好处理,因为是一个新的类,没有地方引用。接着需要修改所有的之前调用DBExecutor的地方,改成通过NoSQLDBExecutor实例化db对象

public class OrderAppService
{
//生成订单
public void CreateOrder(Order order,NoSQLDBExecutor db)
{
//......前略......
db.Insert(order);
}
}

我们不得不修改几乎所有的业务代码类,这个过程繁琐而又容易出错,也违反了封闭原则。

为了避免这样的问题,我们需要将关系依赖倒置,引入一个抽象接口,高层类在实现时依赖抽象接口,低层类也是基于这个抽象接口,这也就是依赖倒置,再实现功能的时候就是高层开始到低层

High Level Classes --> Abstraction Layer --> Low Level Classes

上面这个例子,我们先抽象一个接口

public interface IDBExecutor{
void Create<T>(T data);
void Update<T>(T data);
void Delete<T>(T id);
}

public class OrderAppService
{
//生成订单
public void CreateOrder(Order order,IDBExecutor db)
{
//......前略......
db.Insert(order);
}
}

将来再更换为mysql数据库时,OrderAppService不需要任何改动。

实际操作(开发心得)

我们开发实操时,如果团队分为前台和后台开发,那应该由前台开发来定义我们的接口,而后台来实现接口。

我们的开发有可能是这样的:有个Controller层(高层),有个Service层(低层),Service层有实现,有接口,Controller层会去调用接口,而接口另有实现。

看起来似乎满足我们之前说的依赖倒置原则:高层(Controller)调用抽象(IService),而抽象的实现是Service。但是倒置的核心在于IService是由谁定义的,如果IService是由Service定义(比如先做实现,再抽取接口),那就依然是低层决定高层(Controller),如果IService是由Controller来定义的,才是真正意义上的依赖倒置。

比如我们实现注册功能,根据需求得到原型图如下:

点击【注册】按钮时,实现账号注册功能。

  1. 首先我们会先建一个RegisterController,新增一个接口Register

  2. 根据页面,我们设计Register方法的参数RegisterInput,需要两个手机号和验证码两个属性

  3. 新增接口IRegisterService,新增接口方法Register,参数RegisterInput

  4. Controller引入接口IRegisterService,调用IRegisterService.Register

  5. 新增RegisterService实现IRegisterService

框架中的依赖倒置

什么是框架

框架及时用例实现某一类应用的结构性程序,是对某一类架构方案的可复用设计与实现。框架实现了多重设计模式,使得应用开发者不需要花太大力气,就能设计出结构良好的程序来。

即应用开发者,按照框架设计的规范创建或实现相关代码,框架自然会来调用这些代码,实现代码功能。

这其实就是依赖倒置整个程序的运行是在框架中运行的(高层),而框架约定应用开发者的代码(低层)只要(而且必须要)按照约定的抽象接口实现代码,就能正常运行

框架举例

比如.net的MVC开发框架,就是一套用于开发web站点应用程序的框架。

MVC即Model-View-Controller,Model(模型)组织数据。 View(视图)显示数据。 Controller(控制器)处理输入。

Controller必须放在Controllers文件夹下,命名必须为XXController,每个Controller里面有不同的Action处理不同的请求,会跳转到视图,这个视图在Views文件夹下,文件名同Action。

上面提到了不少必须,约定,这就是当我们在MVC框架基础上进行web开发时必须遵循的抽象约定,只要我们按照这个约定来,一个正常的前台请求就能准确的走到Contorller对应的Action,在处理完数据后跳转到相应的视图进行展示,也就是依赖倒置。

因此有时我们也称依赖倒置原则为

好莱坞原则

Don't call me, I'll call you.

你(低层/应用代码)不要调用我(高层/框架),我(高层/框架)会调用你(低层/应用代码)

参考:

https://www.oodesign.com/dependency-inversion-principle.html

用户头像

金桔🍊

关注

还未添加个人签名 2018.04.11 加入

还未添加个人简介

评论

发布
暂无评论
依赖倒置原则