解耦:哪些方法可以用来解耦代码

news2024/11/15 13:56:47

目录

1.引用

2.为何解耦如此重要

3.如何判断代码是否需要解耦

4.如何给代码解耦

5.思考题


1.引用

        前面我们曾经讲到,重构可以分为大型重构和小型重构。小型重构的主要目的是提高代码的可读性,大型重构的主要目的是解耦。本节讲解如何对代码进行解耦。

2.为何解耦如此重要

        在软件的设计与开发过程中,我们需要关注代码的复杂度问题。复杂的代码经常有可读性、可维护性方面的问题,那么,如何控制代码的复杂度呢?其实,控制代码的复杂度的手段有很多,效果显著的应该是解耦,因为解耦可以使代码高内聚、低耦合。利用解耦的方式对代码进行重构可以有效控制代码的复杂度。

        实际上,”高内聚、低耦合”是一种通用的设计思想,它不仅可以指导细粒度的类之向关系的设计,还能指导粗粒度的系统、架构、模块的设计。相比代码规范,它能够在更高层次上提高代码的可读性和可维护性。

        无论是阅读代码还是修改代码,“高内聚、低耦合”特性可以让我们聚焦在某一模块或类上,不需要过多了解其他模块或类的代码,从而降低阅读代码和修改代码的难度。因为依赖关系简单,耦合度低,所以修改代码时不会牵一发而动全身,代码改动集中,引入bug的风险降低。

        代码“高内聚、低耦合”意味着代码的结构清晰,分层和模块化合理,依赖关系简单,模块或类之间的耦合度低。对于“高内聚、低耦合”的代码,即使某个类或模块内部的设计不合理,代码质量不算高,影响范围也是有限的。我们可以聚焦这个模块或类并进行小型重构相比代码结构的调整,这种改动集中的小型重构的难度大幅降低。

3.如何判断代码是否需要解耦

        如果修改一段功能代码时出现“牵一发而动全身”的情况,那么说明这个项目的代码耦合度过高,需要对其进行解耦。除此之外,我们还有一个直观的衡量方式,就是先把项目代码中的模块之间、类之间的依赖关系画出来,再根据依赖关系图的复杂度来判断项目代码是否需要解耦。如果模块之间、类之间的依赖关系复杂、混乱,那么说明代码结构存在问题,此时,我们可以通过解耦让依赖关系变得简单、清晰。

4.如何给代码解耦

        接下来,我们探讨一下如何给代码解耦。

        1.通过封装与抽象来解耦

        封装和抽象可以应用在多种代码设计场景中,如系统、块、类库、组件、接口和类等的设计。封装和抽象可以有效地隐藏实现的复杂性,隔离实现的易变性,给上层模块提供稳定目易用的接口。

        例如,UNIX系统提供的文件操作函数open()使用简单,但其底层实现复杂,涉及权限控制、并发控制和物理存储等。我们通过将open()封装为一个抽象的函数,能够有效控制代码复杂性的蔓延,将代码复杂性封装在局部代码中。除此之外,因为open()函数基于抽象而非具体实现来定义,所以我们在改动open()函数的底层实现时,并不需要改动依赖它的上层代码。

        2.通过引入中间层来解耦

        中间层能够简化模块之间或类之间的依赖关系。图5-1是引入中间层前后的依赖关系对比图。在引入数据存储中间层之前,A、B和C模块都要依赖内存一级缓存、Redis 二级缓存和DB 持久化存储3个模块。在引入数据存储中间层之后,A、B和C模块只需要依赖数据存中间层模块。从图5-1可以看出,中间层的引入简化了模块之间的依赖关系,让代码结构更加清晰。

        在进行重构时,中间层可以起到过渡作用,实现开发和重构同步进行,且不互相干扰。例如,某个接口的设计有问题,我们需要修改它的定义,于是,所有调用这个接口的代码都要做相应改动。如果新开发的代码也使用这个接口,那么开发与重构之间会产生冲突。为了使重构“小步快跑”,我们可以通过以下4个阶段完成对接口的修改。

        1)第一阶段:引入一个中间层,利用中间层“包裹”旧接口,提供新接口。

        2)第二阶段:新开发的代码依赖中间层提供的新接口。

        3)第三阶段:将依赖旧接口的代码改为调用新接口。

        4)第四阶段:确保所有代码中都调用新接口之后,删除旧接口。通过引入中间层,我们可以分阶段完成重构。由于每个阶段的开发工作量都不会很大,可以在短时间内完成,因此重构与开发发生冲突的概率变小了。

        3.通过模块化、分层来解耦

        模块化是构建复杂系统的常用手段。模块化还广泛用于建筑、机械制造等行业。对于UNIX这样复杂的系统,我们很难掌控其所有实现细节。之所以人们能够开发出UNIX这样复杂的系统,并且能够对其进行维护,主要原因是将该系统划分成了多个独立模块,如进程调度、进程通信、内存管理、虚拟文件系统和网络接口等模块。模块之间通过接口通信,模块之间的耦合度很小,每个小型团队负责一个独立的高内聚模块的开发,最终,将各个模块组合构成一个复杂的系统。

        实际上,模块化思想在SOA(Service-Oriented Architecture,面向服务的架构)、微服务类库,以及类和函数的设计等方面都有所体现。模块化的本质是“分而治之”。

        我们将目光聚焦到代码层面。在开发代码时,我们要有模块化意识,将每个模块都当作个独立的类库来开发,只提供封装了内部实现细节的接口给其他模块使用,这样可以降低模块之间的耦合度。

        除模块化以外,分层也是构建复杂系统的常用手段。例如,UNIX系统就是基于分层思想之间的耦合度。开发的,它大致分为3层:内核层、系统调用层和应用层。每一层都封装了实现细节,并且暴露抽象的接口供上层使用。而且,任意一层部可以被重新实现,不会影响其他层的代码。面对复杂系统的开发,我们要善于应用分展技术,尽量将容易复用、与具体业务关系不大的代码下沉到下层,将容易变动、与具体业务强相关的代码移到上层。

        4.利用经典的代码设计思想和设计原则来解耦

        我们总结一下可以用来解耦的代码设计原则和设计思想。

        (1)单一职责原则

        内聚性和耦合性二者并非相互独立。高内聚使得代码低耦合,而实现高内聚的重要指导则是单一职责原则。如果模块或类的职责单一,那么依赖它们的类和它们依赖的类较少,代码的耦合度也就降低了。

        (2)基于接口而非实现编程

        如果我们利用“基于接口而非实现编程”思想来编程,那么,在有依赖关系的两个模块或类之间,一个模块或类的改动不会影响另一个模块或类。这就相当于将一种强依赖关系(强合)解耦为了弱依赖关系(弱耦合)。

        (3) 依赖注入

        与“基于接口而非实现编程”类似,依赖注入也能将模块或类之间的强耦合变为弱耦合尽管依赖注入无法将本应该有依赖关系的两个类解耦为没有依赖关系,但可以使二者的耦合关系不再像原来那么紧密,方便将某个类锁依赖的类替换为其他类。

        (4)多用组合,少用继承

        继承是一种强依赖关系,父类与子类高度耦合,且这种耦合关系非常脆弱,父类的每一次改动都会影响其所有子类。组合是一种弱依赖关系。对于复杂的继承关系,我们可以利用组合替换继承,以达到解耦的目的。

        (5) LoD

        LoD 的定义描述是:不应该存在直接依赖关系的类之间不要有依赖,有依赖关系的类之间量只依赖必要的接口。从LoD的定义描述中可以看出,使用LoD的目的就是实现代码的低耦合。除上述设计思想和设计原则以外,大部分设计模式也能起到解耦的效果,关于这一部分内容,在我们之前都有讲过。

5.思考题

        实际上,在平时的开发中,解耦到处可见,例如,Spring中的AOP能实现业务代码与非业务代码的解耦,IoC 能实现对象的创建和使用的解耦,除此之外,读者还能想到哪些解耦场景?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1688742.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用python为目录下的文件生成索引

好久没写文章了。 有一个需求: 我的一个目录下有很多的.html文件, 每个html会包含一些image ,但都在各自的目录中。 .html特别多,有好几百个,我需要一个index.hmtl把这些html全部索引起来,使得我一个点击&a…

计算机如何将输入文字显示出来的?渲染Image rendering

1.文字渲染的简单理解 渲染图像,可以理解为用cpu/gpu构造出原本不存在的图像。比如输入计算机的英文字符都是ASCII码,而我们在屏幕上看到显示的字符对应的应该是RGB/YUV的像素。计算机把ASCII字符转化成像素的过程就是文字渲染。又比如我们GPU用多个2D图…

全同态加密生态项目盘点:FHE技术的崛起以及应用

撰文:Chris,Techub News 在当今数字化的时代,隐私保护已成为一个全球性的焦点话题,特别是在加密货币和区块链技术快速发展的背景下。虽然当前的隐私技术在保护数据安全方面多有欠缺,引发了广泛的关注和批评&#xff0c…

如何彻底搞懂装饰器(Decorator)设计模式?

对于任何一个软件系统而言,往现有对象中添加新功能是一种不可避免的实现场景,但这一实现过程对现有系统的影响可大可小。从架构设计上讲,我们也知道存在一个开闭原则(Open-Closed Principle,OCP)&#xff0…

中文信息期刊投稿邮箱

《中文信息》杂志是国家新闻出版总署批准的国家级刊物(月刊),国内外公开发行,大十六开印刷。本刊主要反映我国中文信息处理的学术水平,重点刊登科技、经济、教育等领域的基础理论、科研与应用技术的学术论文&#xff0…

使用Coding部署项目

coding概述:提供一站式开发协作工具,帮助研发团队快速落地敏捷开发与 DevOps 开发方式,实现研发效能升级 一、创建项目 省略 详细文档:https://g-mnbk6665.coding.net/quickstart 二、SSH连接 关于ssh相关命令 重启SSH服务 s…

2023蓝桥杯大赛软件类省赛Java大学B组G题 买二增一 队列的简单应用

用队列 Queue package Dduo; //Bhu Bigdata 1421 //Eslipse IDE 2020-08 //JDK 1.8 //2024/5/19 import java.util.Scanner; import java.math.BigInteger; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue;public class Main {public static v…

【openlayers系统学习】1.6下载要素,将要素数据序列化为 GeoJSON并下载

六、下载要素 下载要素 上传数据并编辑后&#xff0c;我们想让用户下载结果。为此&#xff0c;我们将要素数据序列化为 GeoJSON&#xff0c;并创建一个带有 download​ 属性的 <a>​ 元素&#xff0c;该属性会触发浏览器的文件保存对话框。同时&#xff0c;我们将在地图…

二叉树顺序结构及链式结构

一.二叉树的顺序结构 1.定义&#xff1a;使用数组存储数据&#xff0c;一般使用数组只适合表示完全二叉树&#xff0c;此时不会有空间的浪费 注&#xff1a;二叉树的顺序存储在逻辑上是一颗二叉树&#xff0c;但是在物理上是一个数组&#xff0c;此时需要程序员自己想清楚调整…

GPT-4o: 未来的智能助手

GPT-4o: 未来的智能助手 在这个信息爆炸的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为我们生活中不可或缺的一部分。作为OpenAI最新推出的语言模型&#xff0c;GPT-4o不仅继承了前几代模型的优点&#xff0c;还在多个方面进行了显著的提升。本文将带你深入了解…

C++:vector基础讲解

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《C&#xff1a;vector基础讲解》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 如果本篇文章对你有帮助&#xff0c;还请各位点点赞&#xff01;&#…

网络编程day7

思维导图 数据库编程实现学生管理系统 #include <header.h> #define ID 1 #define NAME 2 #define AGE 3 #define SCORE 4 int do_add(sqlite3 *ppdb) {int add_numb;char add_name[20];int add_age;double add_score;printf("enter student id:");scanf(&quo…

1076: 判断给定有向图是否存在回路

解法&#xff1a; 直观的方法用邻接矩阵dfs,这是错误的代码 #include<iostream> #include<vector> using namespace std; int arr[100][100]; int f 0; void dfs(vector<int>& a, int u) {a[u] 1;for (int i 0; i < a.size(); i) {if (arr[u][i]…

绝缘监测系统在1kV 及以下低压配电系统的应用

安科瑞电气股份有限公司 祁洁 acrelqj 一、系统概述 Acrel-2000L/A 绝缘监测系统设备适用于 1kV 及以下低压配电系统。该设备可以集中采集监测显示绝缘监测仪的数据&#xff0c;实现最多 8 个绝缘监测仪的数据&#xff0c;并且实时记录告警信息和曲线查询。匹配的绝缘监测仪…

bootstrap入门

官方网站&#xff1a;全局 CSS 样式 Bootstrap v3 中文文档 | Bootstrap 中文网 里面各种可以直接用的组件 不全的话可以网上搜索Boostrap常用的按钮样式_btn-large-dim-CSDN博客 怎么在vue项目中使用呢 npm install bootstrap 下载下来然后在main.js加上红框三句后&#…

SpringCloudAlibaba:6.2RocketMQ的普通消息的使用

简介 普通消息也叫并发消息&#xff0c;是发送效率最高&#xff0c;使用最多的一种 依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSch…

1701java药品进销存管理系统Myeclipse开发sqlserver数据库web结构java编程计算机网页项目

一、源码特点 java web药品进销存管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境 为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为s…

解决vue3 vite打包报Root file specified for compilation问题

解决方法&#xff1a; 修改package.json打包命令 把 "build": "vue-tsc --noEmit && vite build" 修改为 "build": "vite build" 就可以了 另外关于allowJs这个问题&#xff0c;在tsconfig.json文件中配置"allowJs&qu…

重学java 39.多线程 — 线程安全

逐渐成为一个情绪稳定且安静成长的人 ——24.5.24 线程安全 什么时候发生&#xff1f; 当多个线程访问同一个资源时&#xff0c;导致了数据有问题&#xff0c;出现并发问题&#xff0c;数据不能及时更新&#xff0c;导致数据发生错误&#xff0c;出现线程安全问题 多线程安全问…

【不太正常的题】LeetCode.232:用栈的函数接口实现队列

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;初阶数据结构刷题 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 &#x1f697; 1.问题描述&#xff1a; 题目中说了只能使用两个栈实现队列&#xff0c;并且只能使用…