5

*重构下载功能模块

 3 years ago
source link: https://mrbenwang.github.io/post/2020/20200622-restructure-download/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

*重构下载功能模块

2020-06-22 10:07

约 2010 字 预计阅读 5 分钟

最近项目新上线的版本中,出现了很多涉及下载相关的bug;经过艰难的代码调试排查,可还是没有准确定位问题。主要还是由于代码年久,经过多代人的修修补补,到处都是“坏味道”的代码。跟项目经理沟通过后,同意重构这部分的代码。下面就简单记录一下重构的过程。

项目最早可追溯至2014~2015年,分为配置管理网站(Server),WPF桌面软件(Client)。桌面软件又分为:用户操作界面WPF,后台服务 Windows Services 程序。
在后端Services中,维护一个本地的小型Firebird数据库,为WPF提供数据和用户状态保持;实现了从远程服务器同步数据到本地,访问远程接口下载管理数据上报等功能。而我这次要做的就是,重写下载管理。下面是关于旧代码的分析:

  1. 三种不同的下载触发机制:软件启动触发用户触发数据同步到本地触发
  2. 八种不同的资源下载,分别在不同的表中管理;
  3. 有五套复制粘贴的代码:软件启动(两套)、用户触发(两套)、数据同步(一套);
  4. 大量无意义尝试,比如下载失败404后,短时间内没有必要再次尝试;
  5. 状态管理不统一,有的下载完成状态改为 ReadyForUse,有的下载完成状态改为 DownloadSuccess;
  6. 执行方法语义重叠,比如 DownloadPause 和 DownloadStop,DownloadFinished 和 DownloadSuccess;
  7. 大段注释代码,这些代码基本永远不会再使用的;

鉴于项目是在运行阶段,一方面要考虑时间成本,另一方面要保证原有的运行方式。在当前软件的框架下,把重写的代码限定在下载模块内部,对外的接口不做改变。
重构代码的抽象设计,也是依据当前项目的实际情况,尽可能的通用和易扩展。设计的UML类图如下:

重构设计的UML

下面的三个虚类,是下载管理流程的核心,所有的控制逻辑都包含在其中。

  1. DownloadResourceAbstract 虚类 资源文件的基本信息: DatabaseIDResourceID为了兼容不同数据库表的设计;DownloadPriority 预设优先级;DownloadImmediatelyPriority 用户触发的下载,优先级最高,立即执行;其它字段涵义可见字段名称;
  2. DownloadWorkItemAbstract 虚类 资源下载操作类,包含下载状态,开始,停止,MD5检查,状态更改的回调方法;与一对一关系;
  3. DownloadControlerAbstract 虚类 控制同时下载的数量,根据优先级控制下载的顺序;与一对多关系

下面的类,是具体下载方法的实现;当前的项目只有HTTP下载,类图中也只实现了HTTP的下载的管理功能:

  • 可扩展下载方法设计:FtpClientHttpClient继承自DownloadClientAbstract
  • 关联 HTTP 下载,分别实现的子类 HttpDownloadWorkItemHttpDownloadControler

对不同类型的资源文件,组合不同的下载方式:

  • AutoHttpDownloadDealer: 自动下载的资源 + HTTP 下载;
  • UserOperHttpDownloadDealer: 用户手动操作的资源 + HTTP 下载;
  • 由数据同步到本地的触发下载资源 + HTTP下载;(未在UML图中画出)

发现bug的真实原因

在重构测试阶段,意外发现了bug的原因:前段时间更换了文件服务器,而新的文件服务器,不支持断点续传,HTTP Status 返回的不是预期的Partial Content(206),而是OK(200);下载文件不存在时,HTTP Status 返回的也不是 404,而返回自定义的 Response Text 。。。

由于这两个不合理的地方,导致软件的下载功能,出了好多莫名其妙的bug,再叠加到处复制 + 粘贴“坏味道”的代码,修复起来异常繁琐,于是就有了重构这件事情。

在当前的项目中,也做了几次小范围的重构,要么代码影响范围小,要么只是在原代码基础上套个框架。而这次是彻底干掉原代码,重新设计,重新写。下面是代码重写完成后 git merge 的 log:

Showing 1107 revision(s), from revision 6092a531 to revision f336c077 - 1 revision(s) selected, 166 file(s) selected; line: 5226(+) 5251(-) files: modified = 118 added = 28 deleted = 20 replaced = 0

从上面的log汇总信息,好像也不足以展示修改工作量。这些修改大约花费了我一周的时间,自愿工作日加班,自愿周末加班(有时候代码写上头了,还得强迫自己下班休息去)。开始的前三天的时间,边读旧代码,边搭建新的框架,尝试把旧代码融入到新设计中;后期转换思路,理解代码逻辑,把代码逻辑融入到新设计中

这样就可以放开手脚的干起来了,写代码的时候,感觉时间过的好快,如有神助般各种代码中的细节,也会不假思索的写出来。在下阶段单元测试中,非常顺滑,基本没有发现什么bug,真的是酣畅淋漓。

最最让我寒心的是,好不容易重构测试完代码了,压抑着无比激动的心情,希望组里的同事给个积极肯定的反馈,可连一个“卧槽”也没有,就行往常平淡的工作日。。。项目经理也是很淡然的说,要尽快拿给测试。

即使我的努力和心血没有得到,预期中的肯定,我也要把这个过程留存记录下来:把重构的代码剔除项目信息,再提取合并通用的部分,重新梳理成一个类库Github 地址;写一篇文章来记录一下。

其实,后面我细细回想了一下。如果最开始项目设计的下载功能是,把所有下载资源放在同一张数据库表中管理,是不是就那不就更简单了?如果最最早期的设计做好了,后期可以省掉多少功能扩展、修复bug、重构。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK