前置知识
- 了解开闭原则 开闭原则|设计原则
- 了解设计模式基础
- 有项目经历
前言
本文带大家学习 依赖反转原则
依赖反转也称为依赖倒置,它的定义是十分抽象的。
首先我们看一下它的英文释义:
High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions.
将上文翻译之后,其含义是这样子的:
高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象
初看这个定义,会觉得很绕,啥是高层模块,啥是低层模块?怎么又通过抽象来相互依赖?等等的这些问题,让我们难以理解上文的释义
下文我们将对此进行介绍和举例,同时介绍与其相近的控制反转,以及依赖注入,相信阅读之后,你对依赖反转会有更深的理解
依赖反转
理解 依赖反转 的定义,首先我们回答一下几个问题。
- 何为高层模块、低层模块?
高层模块我们可以理解为调用方,而低层模块则理解为被调用方。 - 如何通过抽象来相互依赖?
即调用者与被调用者之间无直接依赖关系,而是两者都遵从某种协议、某些抽象类、或是某些接口 - 如何只由实现细节依赖抽象?
这一点是符合抽象的定义的,抽象只负责顶层指导,而由对应的继承者来实现这些抽象
解释完上面的问题,我们会发现,这个依赖反转原则与“基于接口而非实现编程”有些类似。事实上,该原则的核心就是“基于接口而非实现编程”。依赖反转通俗来说就是,把高层依赖低层反转一下,变成高层底层都依赖抽象。
但是,依赖倒置原则 在业务开发中并不常见,因为业务开发中 高层模块直接依赖低层模块 是极为常用且正常的。更多的是使用到其核心,“基于接口而非实现编程”。依赖倒置原则更侧重与指导框架的设计,例如我在 带你封装MVP架构(下) 一文中,就使用到了依赖倒置原则。
相关的依赖倒置举例如下
Tomcat
Tomcat 是运行 Java Web 应用程序的容器。我们编写的 Web 应用程序代码只需要部署在 Tomcat 容器下,便可以被 Tomcat 容器调用执行。按照之前的划分原则,Tomcat 就是高层模块,我们编写的 Web 应用程序代码就是低层模块。Tomcat 和应用程序代码之间并没有直接的依赖关系,两者都依赖同一个“抽象”,也就是 Servlet 规范。Servlet 规范不依赖具体的 Tomcat 容器和应用程序的实现细节,而 Tomcat 容器和应用程序依赖 Servlet 规范。
Room封装使用
数据库案例也符合
依赖倒置原则
,高层模块(业务层)不依赖于低层模块(SQLiteDao/RoomDao),而是依赖于抽象(IDao)——开闭原则设计
控制反转
控制反转 缩写为IOC(Inversion Of Control),简单解释其意思为:把控制权反转过来
具体是把控制权从谁手中反转过来呢?从程序员手中。那反转到谁那里呢,反转到代码框架那里。
这个概念的本意是,程序员在编写代码的时候,编写了设置了整个代码流程,此时控制权在程序员手中。
当对代码抽象框架化,代码拓展性提高,且最终由框架实现代码流程。程序员只输入少量代码启动代码流程,这个就是符合控制反转思想。
框架提供了一个可扩展的代码骨架,用来组装对象、管理整个执行流程。程序员利用框架进行开发的时候,只需要往预留的扩展点上,添加跟自己业务相关的代码,就可以利用框架来驱动整个程序流程的执行。
但是控制反转只是一种设计思想,具体的实现方式各式各样。前文 开闭原则 的demo中,其设计也是符合控制反转原则的,最终是由框架自行驱动程序。
对应的,LiveData
也是一种控制反转思想的变体,与 MVP
的由 P层
驱动 View
层一样,使用数据驱动 UI。
依赖注入
依赖注入 与控制反转不同的是,依赖注入是一种实现控制反转的具体方法。
其意思为:把被依赖项通过参数等的形式,注入到类中
具体实现如下
public interface ImageGetter { void get(String imageUrl); } public class PictureStore { private ImageGetter imageGetter; public PictureStore(ImageGetter imageGetter) { this.imageGetter = imageGetter; } public void imageGet(String imageUrl) { this.imageGetter.get(imageUrl); } } // 从aliyun获取 public class AliyunGetter implements ImageGetter { @Override public void get(String imageUrl) { //.... } } // 从Tencent云获取 public class TecentGetter implements ImageGetter { @Override public void get(String imageUrl) { //.... } } 复制代码
//使用图床类 ImageGetter imageGetter = new TecentGetter(); PictureStore pictureStore = new PictureStore(imageGetter);//依赖注入 pictureStore.imageGet("https://..."); 复制代码
上述的demo就是实现了依赖注入,把被调用的类通过构造函数注入了对应的类中。对应的控制、代码流程已经反转到框架来实现,所以说,依赖注入实现了控制反转,且其为控制反转的一种重要实现方式。
但是我们也发现了,虽然说控制权反转给了框架,程序员只需做对应的拓展和启动。但是终究这个框架还是要我们自己来实现,当项目变大的时候,我们还是会容易出错的,所以后端开发中常使用 spring boot
这种控制反转容器。这类称之为,依赖注入的框架,在Android中,我们也有常用的依赖注入框架,帮助我们更好的做依赖注入。Dagger2 就是 Google 开发的一款依赖注入框架,大家可自行了解。
三者区别
依赖反转原则是一种设计原则,与控制反转类似,都是框架层面的设计指导。只不过两者作用的对象不同,一个是依赖方,一个是控制权。
而依赖注入则纯为一种实现控制反转的方法,是一种规定好的技巧形式。