写点什么

与其硬啃“屎山”代码,不如用这六步有条不紊实现代码重构

  • 2022-04-28
  • 本文字数:2851 字

    阅读完需:约 9 分钟

与其硬啃“屎山”代码,不如用这六步有条不紊实现代码重构

对大规模系统进行重构,如果一个人对着又臭又长的代码硬刚,即使花了大量的时间进行手工验证,最后仍然会有很多问题,特别是一些深路径及特殊场景下的问题。其实,大规模的系统级别重构时是有方法的。我们采访了 Thoughtworks 数字化转型与运营 资深咨询师黄俊彬(QCon+案例研习社讲师),请他来分享 MV*模式重构演进的方法和经验。

 

InfoQ:请问,开发人员什么时候需要进行代码重构呢?

 

黄俊彬:重构的范围很广,基于实际对代码的调整的工作量及影响范围,可以细分为表中 3 种类型:


类型

修改范围

示例

小型重构

对单个类内部的重构优化

重命名、提取变量、提取函数等

中型重构

对多个类间的重构优化

提取接口、超类、委托等

大型重构

针对系统组件架构重构优化

单体应用组件化等


由于不同类型的重构影响及工作量不同,重构时机也不同:

 

  • 小型重构,建议任何阶段及时重构,同时借助 IDE 提供的自动化重构,保证安全。

  • 中型重构,建议在添加新功能或者修复 Bug 时集中设计及修改,需做好测试。

  • 大型重构,特别针对大型的遗留系统改造,建议立专项,结合业务的迭代需求,将大型的重构进行拆分,在不同的研发迭代中进行。


InfoQ:您在 QCon+分享了 MV* 模式重构演进,请问在重构的过程中,有什么通用的流程吗?


黄俊彬:在 MV*模式重构演进分享中,我将重构分为了 3 个维度 6 个步骤,如下图所示:



对于遗留系统来说,通常最常见的问题就是需求的上下文存在断层,所以第一步需要尽可能地分析了解原有的业务需求。


第二步是需要仔细去分析原有的代码设计,分析原有代码的设计及存在的坏味道,作为重构后的改进项及有优化点。


第三步,补充守护测试。在开始进行重构时先补充中大型的自动化测试将主要的业务场景进行覆盖,每当小步进行安全重构时都能运行测试进行快速的反馈,更好地减少修改代码导致的风险。


第四步是进行简单设计,结合未来架构的设计模式,设计关键的接口及数据模型,作为重构的输入。在这一步通常先结合未来的架构设计划分出主要的分层,例如 MVP 模式会有 Model、View 及 Presenter 层。接下来需要重新梳理领域模型,重新划分出合适的模型。最后需要定义出层之间关键的接口,例如 MVP 中 View 及 Presenter 之间交互及会调的接口。


第五步,进行小步安全重构。在重构的过程中尽可能运用前面重构基础中介绍的安全重构手法,减少人工修改,并且在过程中尽可能做到小步提交及借助测试进行频繁验证,逐步将代码修改为设计的架构模式。


第六步,集成验收测试。重构后的代码需要保证编译通过、所有的自动化测试运行通过及进行集成的验证。


InfoQ:您认为在重构过程中,如何更好提高效率及安全性?


黄俊彬:重构如果在没有好的保障机制下,很容易引起新的问题。这里有 4 种在重构过程中常用的技术实践,可以更安全及高效地对代码进行重构。


第一是结对进行重构,通过实时 Review 保障正确性。


例如在对某大型的通讯类系统进行重构时,我与客户的架构师一起结对进行重构。因为客户的架构师熟悉业务及原有代码的上下文,在结对进行重构时的一些上下文能实时进行确认,能更有效地保障重构的安全性。


第二是尽可能自动化,借助 IDE 的安全重构进行优化,避免手工修改代码的风险。


在进行大型重构时,我们经常需要移动相关的代码及类到合适的位置。特别是当移动的类有关联到其他相关的类的时候,手工进行移动风险非常大,效率也非常低。


在这种场景下,我们可以借助 IDE 的 Move 或者 Moduriaze(Android Studio 带的功能)进行重构,像 Moduriaze 能够智能分析出移动的类及其关联的所有类与资源,一把进行自动化的移动,大大提高了安全性及效率。


第三是增加测试保护,借助自动化测试,在每次重构时频繁执行进行保障,及时反馈。


这里要特别说明的是,很多遗留的系统由于代码的腐化,存在大量的上万行的类及大几千的方法,比较难编写单元测试。在对遗留系统增加的自动化测试的策略上,通常会先覆盖中大型的接口测试或者 UI 测试,守护核心的业务逻辑。在重构完成后,再及时补充小型的单元测试。


第四是小步前进,让每笔提交尽可能小,方便进行问题定位及回滚。


InfoQ:如何更好去度量重构的结果呢?


黄俊彬在重构的过程中,一定要以终为始,思考清楚重构是为了解决什么样的问题,建立有效的度量反馈机制。例如希望将遗留代码重构成为新的架构模式,我们就必须度量重构后的代码是否符合新的架构设计模式,并且尽可能通过自动化的方式进行约束及统计。比如重构为了简化代码的复杂度,如过长的类及方法,我们就必须度量重构的代码复杂度,如圈复杂度、类及方法长度、重复率等。


实际上,通过度量反馈机制,可以让重构的价值更显性比如,中小型的重构可以通过观察代码的健康度相关的指标来显性重构的价值,如重构后代码的圈复杂度、平均函数行数、类行数等指标。大型的重构可以通过工程效率上的指标来显性重构的价值,如组件化后的编译速度的提升,组件化后发布的效率等。


InfoQ:代码重构有哪些常见阻碍因素呢?该如何解决呢?


黄俊彬:重构落地的阻碍因素,我认为主要受下面三个因素影响。


1. 重构带来的价值不够显性。重构后不会改变软件可观察行为,其价值不够显性,往往优先级都会排列在业务迭代之后。


2. 没有足够的重构时间窗口。在实际项目中不可能将业务停下来专门预留充足的时间窗口来进行重构,结合上面提到的重构时机,中小型的重构还是需要结合在日常的开发过程中,化整为零。


3. 缺少激励,风险高。如果组织中缺少这方面的文化及保障机制,重构往往让技术人员望而却步。一方面需要结合有效的度量指标来引导技术人员进行重构,另一方面也需要从组织上做好激励机制,鼓励将重构工作也作为技术故事排入迭代开发中来。


InfoQ:你印象最深的一次遗留系统重构实践是什么?为什么令你印象深刻?


黄俊彬:我印象最深的的一次遗留系统重构是给一个客户做一个超级 App 重构,这次重构主要是进行架构上的优化。让我印象比较深刻的点是因为这个应用规模比较大,代码在百万行级别,开发人员也达到 200+,全量编译构建达到 15 分钟以上。


在系统分析的过程中,我们发现业内像 Sonar 等工具更多是对现有的代码进行通用的代码坏味道扫描,对于遗留系统的大型重构通常需要将现有的系统重构为新的架构模式,所以必须以未来的架构进行输入去梳理出当前系统需要解除的耦合。


基于这个诉求我们自己开发了工具,以未来的架构作为输入,对代码进行扫描分析,梳理出依赖清单,然后基于依赖清单进行重构。


在这个案例中,最后一个业务组件解耦独立出来后,一次组件的构建调试只需要 1 分钟。


嘉宾介绍:黄俊彬,Thoughtworks 数字化转型与运营 资深咨询师,10 年软件开发、设计和架构经验。在移动开发领域的应⽤性能优化、自动化测试、架构设计及组件化等⽅向有丰富的经验。目前主要在智能硬件、通信、互联网、金融等领军企业提供敏捷转型、性能优化、系统架构改造、大型遗留系统重构等服务。


活动推荐:今年 7 月,ArchSummit全球架构师峰会将落地深圳站。官网现已上线研发效率提升、可观测性技术落地探索、出海业务架构、单云架构到多云架构转型、微服务架构落地实践、架构师能力模型、开源和自研选型思考、云原生前沿技术、IoT 系统架构设计、金融领域数字化转型等十余个热门专题,扫描下方二维码即可查看精彩内容。


现在购票可享 8 折特惠,单人购票立减 1760 元,团队购票优惠更多。购票请扫码或咨询:18514549229(微信同电话)



2022-04-28 14:555006

评论

发布
暂无评论
发现更多内容

打工人的第27天-平凡但不平淡的日子

Amazing_eve

#开源

呵呵,JavaScript 真好玩(苦笑脸)

掘金安东尼

JavaScript 前端 8月月更

从脚本到剪辑,影像大师亲授的后期制作秘籍

博文视点Broadview

2022年裁员潮,失业程序员何去何从?

千锋IT教育

谷歌数据中心发生“电力事故”造成 3 人受伤

雨果

数据中心 谷歌

企业如何判断数据治理是否成功?

雨果

数据治理

快速上手,征服三种不同分布式架构调用方案

知识浅谈

分布式 8月月更

中小规模网站架构

舟停江吹雪

Linux

2022秋招前端面试题(十)(附答案)

helloworld1024fd

8月份DB-Engines 数据库排行榜最新战况

雨果

数据库

使用CSS实现多种Noise噪点效果

dragonir

CSS html html5 css3

GPU加速Pinterest推荐模型,参数量增加100倍,用户活跃度提高16%

OneFlow

机器学习 深度学习 gpu

Gitlab刚发布一项禁止使用 Windows 的公司政策

雨果

gitlab Github'

OneFlow源码解析:算子指令在虚拟机中的执行

OneFlow

虚拟机 源码解析 算子

2022秋招前端面试题(九)(附答案)

helloworld1024fd

短视频软件开发——平台同质化如何破局

开源直播系统源码

软件开发 直播源码 短视频直播源码 短视频直播系统源码

学Python爬虫,不看看m3u8文件如何加密?i春秋 m3u8 文件加密解析

梦想橡皮擦

Python 爬虫 8月月更

是什么影响了MySQL性能?

TimeFriends

8月月更

50个Java面试必问的面试题,这里都给你整好了

千锋IT教育

如何正确理解线程机制中常见的I/O模型,各自主要用来解决什么问题?

PivotalCloud

Linux Linux Kenel

Linux服务器端网络抓包和分析实战

程序员欣宸

Java Linux 8月月更

STM32封装ESP8266一键配置函数:实现实现AP模式和STA模式切换、服务器与客户端创建

DS小龙哥

8月月更

C++运算符重载(二)之左移运算符重载

CtrlX

c c++ 进阶 重载 8月月更

JWT 实现登录认证 + Token 自动续期方案

CRMEB

开源一夏 | mysql5.7安装部署-yum安装

zhangpfly

MySQL 开源 linux运维 #开源 8月月更

一起畅聊「云+操作系统」!龙蜥社区亮相阿里巴巴开源开放周,完整议程来了

OpenAnolis小助手

数据库 操作系统 龙蜥社区 阿里巴巴开源开放周 开源共享

一文教会你快速上手 Vim

昆吾kw

vim Linux

面试突击73:IoC 和 DI 有什么区别?

王磊

Java 常见面试题

Gartner再次重申了“数据编织”的重要价值

雨果

数据编织

SQL与NoSQL最终会走向融合吗?

雨果

nosql sql

Open Office XML 格式里如何描述多段具有不同字体设置的段落

汪子熙

xml 微软 Office 8月月更 openOffice

与其硬啃“屎山”代码,不如用这六步有条不紊实现代码重构_文化 & 方法_李慧文_InfoQ精选文章