0x00 引言
软件重构包括代码重构、模块重构与架构重构,难度依次提升,且越靠后的一般越少改动。推荐阅读《重构 改善既有代码的设计》。
0x01 代码重构
一个代码片段的重构通常包括函数重构与函数内部重构。在重构过程中,几乎有10%的工作为修改变量、函数、类与文件名,20%的工作为函数化,最主要的70%的工作是在修改函数内部代码逻辑。
1 | 1 函数重构 |
1、函数重构
函数重构包括提炼函数与合并函数。
提炼函数是一种将现有函数内的一部分代码提取出来,形成一个新的函数的重构技术。通常需要被提炼的函数具有多判断语句、单一或多返回值的特点。
合并函数是一种将调用某个函数的地方直接替换为该函数的函数体内容的重构技术。这通常用于简化代码,去除不必要的函数调用,特别是当函数的功能较为简单或不再需要时。
2、函数内部重构
函数内部重构就是指如何优化函数内部逻辑,其流程通常是:1.异常情况尽早return;2.尽量删除冗余的临时变量;3.按功能将代码分成多个语句块,并添加注释;4.语句块间逻辑清晰,尽量使一段函数一屏展示。
3、重构技巧
诊断技巧 | 重构技巧 | |
---|---|---|
函数重构 | 研究两个相似函数,发现大量代码相同、少量代码不同 | 合并代码重复的函数 |
研究两个相似函数,发现代码功能相同、局部语句或变量不同 | 合并功能类似的函数,加参数判断 | |
提炼针对局部变量进行加工的函数 | ||
发现多个函数整体算法相同、数据不同,合并、改为宏或模板 | ||
函数内部 | 研究变量引用,发现变量读语句分散各处 | 发现孤立的两个if实则互斥,改为else if结构 |
研究变量引用,发现变量写语句分散各处 | 发现孤立的多个if实则互斥,改为switch结构 | |
研究变量引用,发现 变量名含义不准确 | 发现if-else中特殊处理极短,特殊处理提前return | |
研究变量引用,发现 变量赋值后含义变了 | ||
研究最终执行语句,发现判断嵌套太深 | ||
研究最终执行执行,发现判断条件复杂难懂 |
0x02 模块重构
新手架构师通常认为模块划分越细、单个模块越简单越好,实际上应该把核心模块做精细、做聪明,能减轻大量普通模块的开发工作。
模块重构的探讨范围仍在用户态,此章将把用户态程序根据常用功能划分为六大模块结构模式,这些模式可作为模块开发的模板。要重构模块,首先需要理解程序运行流程,如下图
1 | 1 代码抽象 |
1、代码抽象
代码抽象 = 数据结构抽象 + 函数抽象 + 模块抽象。本章节重点要讲的就是模块抽象。
- 数据结构抽象:将多个较简单的值组合在一起,形成一个单一的单元,例如结构体。
- 函数抽象:将一组操作封装到函数或方法中,以实现代码的模块化和可重用性。
- 模块抽象:模块抽象涉及到将相关功能和数据封装到模块中,从而实现代码组织和管理的抽象。
2、模块抽象
现在根据常用的编程技巧,可将程序抽象成为以下六大模块结构模式:纯功能模块、数据管理模块、调度中心模块、事件驱动模块、时钟驱动模块、周期执行模块。这些模式可在开发过程中作为模块开发的模板。值得一提的是,事件驱动相关的三个模块自下往上一定程度上可看作是越来越复杂的订阅发布模式(外界均需要注册回调到这些模块内部,在某个时机调用)。
3、代码控制流
实际上上述六种模块结构模式,其分类依据很大程度上借鉴了程序设计的代码控制流。也正是因为无系统主机不能调度进程、无中断功能(只能使用轮询),所以我们研究架构重构的范围也被限定在了带系统主机的用户态。
方法 | 描述 |
---|---|
进程调度 | 控制不同进程之间的执行顺序的操作系统机制 |
中断 | 用于处理特定事件的硬件或软件生成的信号 |
系统调用 | 进程请求操作系统执行特定任务的接口 |
信号 | 异步事件通知方式,可暂停进程并执行信号处理程序 |
多线程 | 多个线程并发运行,每个线程有自己的控制流 |
锁机制 | 控制对共享资源的访问,用于同步多个进程/线程 |
条件变量 | 用于线程同步的机制,允许线程等待条件的发生并继续执行 |
跳转指令 | 允许程序员显式地改变控制流的指令,通常不鼓励使用 |
4、六大模块
下面详细介绍六大模块: 纯功能模块、数据管理模块、调度中心模块、事件驱动模块、时钟驱动模块、周期执行模块。
4.1、数据管理模块
数据管理模块接口通常包含以下接口函数:
接口函数 | 描述 |
---|---|
管理接口 | 创建、销毁数据管理类的接口 |
IPO 接口 | 输入接口 (I):接收外部数据,为数据管理类提供输入 |
处理接口 (P):处理、分析输入数据,执行核心功能 | |
输出接口 (O):提供经过处理的数据结果,供外部使用 | |
便捷接口 | 一般是用于封装成重复调用IPO接口的函数 |
0x03 架构重构
软件架构通常指的是一种高级结构,用于定义和组织一个系统中各个部分(模块)的相互关系,以实现系统的目标和需求。
阅读成熟开源代码的架构有助于我们设计出优秀的代码架构,此章节以CrazyFile为例(FreeRTOS系统下著名的开源四旋翼项目)。但是需要理解的一点是,源代码并不是架构,很多业务代码在分析架构时是可以不关注的。
在实际的架构设计中,不同的操作系统与设计目标,架构之间的区别是非常大的,下图大致将架构分为四类: