【设计模式之美】 SOLID 原则之五:依赖反转原则:将代码执行流程交给框架

本文主要是介绍【设计模式之美】 SOLID 原则之五:依赖反转原则:将代码执行流程交给框架,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一. 控制反转(IOC)
  • 二. 依赖注入(DI)
  • 三. 依赖注入框架(DI Framework)
  • 四. 依赖反转原则(DIP)

一. 控制反转(IOC)

通过一个例子来看一下,什么是控制反转。

public class UserServiceTest {public static boolean doTest() {// ... }public static void main(String[] args) {//这部分逻辑可以放到框架中if (doTest()) {System.out.println("Test succeed.");} else {System.out.println("Test failed.");}}
}

改造为框架来实现同样的功能,如下:

// 将流程控制逻辑放到testcase类中,形成一个小的流程框架//1. 所有的逻辑类都继承这个类,抽象:以便将创建流程框架
public abstract class TestCase {public void run() {if (doTest()) {System.out.println("Test succeed.");} else {System.out.println("Test failed.");}}//可以按需实现不同被测试逻辑public abstract boolean doTest();
}public class JunitApplication {
//2. 通过组合的方式 ,统一执行所有的逻辑类private static final List<TestCase> testCases = new ArrayList<>();public static void register(TestCase testCase) {testCases.add(testCase);}// main在框架中写一遍,即可执行所有的逻辑类public static final void main(String[] args) {for (TestCase case: testCases) {case.run();}}

把这个简化版本的测试框架引入到工程中之后,我们只需要在框架预留的扩展点,也就是 TestCase 类中的 doTest() 抽象函数中,填充具体的测试代码就可以实现之前的功能了,完全不需要写负责执行流程的 main() 函数了。

具体的代码如下所示:

//
public class UserServiceTest extends TestCase {@Overridepublic boolean doTest() {// ... }
}// 通过传参的方式将自己的业务代码放到框架中,框架管理执行流程
JunitApplication.register(new UserServiceTest();

刚刚举的这个例子,就是典型的通过框架来实现“控制反转”的例子。有两点:

  1. 框架提供了一个可扩展的代码骨架,用来组装对象、管理整个执行流程
  2. 程序员利用框架进行开发的时候,只需要往预留的扩展点上,添加跟自己业务相关的代码,就可以利用框架来驱动整个程序流程的执行。

这里的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。

 

控制反转用于指导架构层面的设计

实际上,实现控制反转的方法有很多,除了刚才例子中所示的类似于模板设计模式的方法之外,还有马上要讲到的依赖注入等方法,所以,控制反转并不是一种具体的实现技巧,而是一个比较笼统的设计思想,一般用来指导框架层面的设计。

 

二. 依赖注入(DI)

依赖注入跟控制反转恰恰相反,它是一种具体的编码技巧。

用一句话来概括DI含义:

不通过 new() 的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用

举例说明:

// 1. 非依赖注入实现方式
public class Notification {private MessageSender messageSender;public Notification() {this.messageSender = new MessageSender(); //此处有点像hardcode}public void sendMessage(String cellphone, String message) {//...省略校验逻辑等...this.messageSender.send(cellphone, message);}
}public class MessageSender {public void send(String cellphone, String message) {//....}
}
// 使用Notification
Notification notification = new Notification();// 2. 依赖注入的实现方式
public class Notification {private MessageSender messageSender;// 通过构造函数将messageSender传递进来public Notification(MessageSender messageSender) {this.messageSender = messageSender;}public void sendMessage(String cellphone, String message) {//...省略校验逻辑等...this.messageSender.send(cellphone, message);}
}
//使用Notification
MessageSender messageSender = new MessageSender();
Notification notification = new Notification(messageSender);

通过依赖注入的方式来将依赖的类对象传递进来,这样就提高了代码的扩展性,我们可以灵活地替换依赖的类。

 

优化:基于接口编程
可以基于自己的需求来传递自己想通知的方式(短信、站内),提高代码灵活性和易拓展性。

public class Notification {private MessageSender messageSender;public Notification(MessageSender messageSender) {this.messageSender = messageSender;}public void sendMessage(String cellphone, String message) {this.messageSender.send(cellphone, message);}
}public interface MessageSender {void send(String cellphone, String message);
}// 短信发送类
public class SmsSender implements MessageSender {@Overridepublic void send(String cellphone, String message) {//....}
}// 站内信发送类
public class InboxSender implements MessageSender {@Overridepublic void send(String cellphone, String message) {//....}
}//使用Notification:基于自己的需求来传递自己想通知的方式,提高灵活性和拓展性
MessageSender messageSender = new SmsSender();
Notification notification = new Notification(messageSender);

实际上,你只需要掌握刚刚举的这个例子,就等于完全掌握了依赖注入。尽管依赖注入非常简单,但却非常有用,它是编写可测试性代码最有效的手段。

 

三. 依赖注入框架(DI Framework)

什么是“依赖注入框架”。继续借用刚刚的例子来解释。

上述例子还是存在需要手动new的情况

在采用依赖注入实现的 Notification 类中,虽然我们不需要用类似 hard code 的方式,在类内部通过 new 来创建 MessageSender 对象,但是,这个创建对象、组装(或注入)对象的工作仅仅是被移动到了更上层代码而已,还是需要我们程序员自己来实现。

public class Demo {public static final void main(String args[]) {MessageSender sender = new SmsSender(); //创建对象Notification notification = new Notification(sender);//依赖注入notification.sendMessage("13918942177", "短信验证码:2346");}
}

通过框架来自动化创建对象:

实际上,现成的依赖注入框架有很多,比如 Google Guice、Java Spring、Pico Container、Butterfly Container 等。

 

四. 依赖反转原则(DIP)

有了前面的基础,我们将依赖反转原则。

定义:

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.

高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。

所谓高层模块和低层模块的划分,简单来说就是,在调用链上,调用者属于高层,被调用者属于低层

 

实际上,这条原则主要还是用来指导框架层面的设计,跟前面讲到的控制反转类似。我们拿 Tomcat 这个 Servlet 容器作为例子来解释一下。

Tomcat 是运行 Java Web 应用程序的容器。我们编写的 Web 应用程序代码只需要部署在 Tomcat 容器下,便可以被 Tomcat 容器调用执行。

  1. 按照之前的划分原则,Tomcat 就是高层模块,我们编写的 Web 应用程序代码就是低层模块。
  2. Tomcat 和应用程序代码之间并没有直接的依赖关系,两者都依赖同一个“抽象”,也就是 Servlet 规范。
  3. Servlet 规范不依赖具体的 Tomcat 容器和应用程序的实现细节,而 Tomcat 容器和应用程序依赖 Servlet 规范。

 

参考:《设计模式之美》-- 王争

这篇关于【设计模式之美】 SOLID 原则之五:依赖反转原则:将代码执行流程交给框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/584845

相关文章

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

Spring核心思想之浅谈IoC容器与依赖倒置(DI)

《Spring核心思想之浅谈IoC容器与依赖倒置(DI)》文章介绍了Spring的IoC和DI机制,以及MyBatis的动态代理,通过注解和反射,Spring能够自动管理对象的创建和依赖注入,而MyB... 目录一、控制反转 IoC二、依赖倒置 DI1. 详细概念2. Spring 中 DI 的实现原理三、

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

python中poetry安装依赖

《python中poetry安装依赖》本文主要介绍了Poetry工具及其在Python项目中的安装和使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前言1. 为什么pip install poetry 会造成依赖冲突1.1 全局环境依赖混淆:1

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

Nginx、Tomcat等项目部署问题以及解决流程

《Nginx、Tomcat等项目部署问题以及解决流程》本文总结了项目部署中常见的four类问题及其解决方法:Nginx未按预期显示结果、端口未开启、日志分析的重要性以及开发环境与生产环境运行结果不一致... 目录前言1. Nginx部署后未按预期显示结果1.1 查看Nginx的启动情况1.2 解决启动失败的

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指