聊一聊:一个大型软件系统该如何重构

开发 架构
在互联网行业,每当新员工入职一家新公司时,都要学习一套新的软件系统。如果该公司的代码非常规范,架构设计非常合理,那么新员工上手的速度会非常快。

1、 为什么需要重构

在互联网行业,每当新员工入职一家新公司时,都要学习一套新的软件系统。如果该公司的代码非常规范,架构设计非常合理,那么新员工上手的速度会非常快。当然,你这个螺丝钉的角色也就非常明显了。另一方面,如果面对『屎山』一样的祖传代码,就会有很多抱怨,学习起来也很痛苦。

从质量上,我把软件大致分为以下几种类型:

  • 第一种:它们对稳定性、规范性要求非常高,所以代码中异常判断、校验非常多,代码看上去就很冗余,最典型代表是华为的电信级产品。
  • 第二种:写得就很随意,风格各式各样,代表的例子是开源项目。
  • 第三种:也是最糟糕的,员工在 PM(产品经理)的高压下日夜加班赶出来的,毫无设计风格,能跑就行,追求最快速度地实现。

重构应该也是互联网公司开发工作的一部分吧。当一个软件系统需要重构时,必然是因为以下某种原因:比如二次开发难度越来越大、新员工上手越来越困难,每个模块只有特定的人懂;面对日益增长的功能需求,现有架构已经满足不了;模块耦合非常严重,导致不同的开发团队之间互相依赖,严重阻碍了开发进度。

1.1 二次开发的难度越来越大、新员工上手成本越来越高

模块之间耦合严重,类间调用关系形成双向依赖。大量的 if/else 分支、运行流程分支太多,圈复杂度爆表,根本理不清。当我们想要增加一个新功能,即使这个功能很小很小,牵扯的链条也非常长,涉及需要改动的周边函数、文件数量巨多。

1.2 腐朽的软件架构无法满足日益增长的功能需求

软件系统从最初的 demo 开始,不断完善,一点点添加新功能,以适应不同的应用场景。比如滴滴这款软件,最初只要把司机端和乘客接进来就行,加上调度系统负责派单,用户规模也比较小;后来,用户规模陡增,需要扩展服务器数量,又牵扯到负载均衡、消息中间件、高并发等需求  ;再后来,添加各种服务类型,比如顺风车、专车、豪华车等等,然后又是各种红包、优惠券等。

1.3 模块耦合严重,无法上云

微服务的前提条件就是模块间能解耦,这不仅是上云的需求,也能提高研发团队整体的开发效率,更重要的是为了实现服务编排,可以给任意子的服务提供灵活的资源,从而最大化集群的资源利用率,也就是说能更好地做到弹性扩缩容和容错。

1.4 新功能一起考虑进去

传统概念中对代码重构的理解是『不引入任何新功能』。我的看法是,代码重构和新功能开发结合起来,这样更有利于最大化重构效果。

2、 重构设计的指导原则

重构也是软件架构设计的一种,这里我称之为『重构设计』。

首先,你要清楚重构的目标是什么。比如侧重满足二次开发,或者侧重模块解耦,或者兼容各种硬件平台、编程语言等等。

其次,你要对基本的软件架构和软件设计风格有清晰的了解,以下是一些必备技能:

2.1、unix 编程艺术

  • 模块原则:使用简洁的接口拼合简单的部件
  • 清晰原则:清晰胜于技巧
  • 组合原则:设计时考虑拼接组合
  • 分离原则:策略同机制分离,接口同引擎分离
  • 简洁原则:设计要简洁,复杂度能低则低
  • 吝啬原则:除非却无他法,不要编写庞大的程序
  • 透明性原则:设计要可见,以便审查和调试
  • 健壮原则:健壮源于透明与简洁
  • 表示原则:把知识叠入数据以求逻辑质朴而健壮
  • 通俗原则:接口设计避免标新立异
  • 缄默原则:如果一个程序没什么好说的,就沉默
  • 补救原则:出现异常时,马上退出并给出足够多的错误信息
  • 经济原则:宁花机器一分,不花程序员一秒
  • 生成原则:避免手工hack,尽量编写程序去生成程序
  • 优化原则:雕琢前要先有圆形,跑之前先学会走
  • 多样性原则:绝不相信所谓”不二法门“的断言
  • 扩展原则:设计着眼未来,未来总比预想来得快

2.2、 设计模式 + SOLID

23 种设计模式,你不一定要完全了解代码怎么写,但一定要知道每一种设计模式背后的设计思想是什么。有一段时间,我试图在我的代码应用各种设计模式,可最终代码看起来特别冗余而且不是那么必要。从个人经验上来讲,平时业务代码中用设计模式的场合非常少,最常用的无非是工厂、适配器、责任链等,而且效果并没那么大,设计模式真正适合的场合是更高层级的,比如模块间设计等等。

单一职责、开闭原则、里氏替换、迪米特法则、接口隔离、依赖倒置。

2.3、领域驱动设计(DDD)

有了解过一些,没有亲身实践过。它大体上就是模块解耦和分层的思想:API(对外访问层)、Domain(领域层)、Repository(数据源访问代理层)及基础设施层(DB、Redis、HTTP、RPC 等)

2.4、逐行代码的重构方法

参考马丁.福勒那本经典的《重构:改善代码的设计》,比如过长的函数、过长的参数、数据泥团怎么处理等等。

这里,也说下个人的小建议:比如纵向的调用关系变为横向的,减少函数调用栈深度;不要过度封装。相信用户能找到底层类的实现接口;逻辑上相关的代码物理上尽可能放在一块;对于某个小的具体功能,涉及的链条越短越好;面向接口编程;访问 A 表数据的 class 中不能存在访问 B 表数据的 function;模块对外暴露的接口部分,数据类型的选择上尽量做到宽进严出(接口要考虑通用性);写操作接口,接收参数尽可能少;读操作接口,返回参数尽可能多;减少不必要的类和数据结构等等。

2.5、微服务

2.6、云原生

毫无疑问,云是未来数字世界的基础设施。

2.7、插件思想

比如约定/注入插件、事件插件、插槽插件等等。

好了,这里不再列举了,因为根据哈弗大学心理学博士米勒的研究,对于每一类产品,用户最多只能记住七类品牌,这里我也就列七个重构原则~

3、 如何撰写重构设计文档

以面向对象语言为例,这里我把它分为了几个步骤:

  • 旧系统的类间调用关系图 vs 新系统的类间调用关系图
  • 旧系统的整体架构图 vs 新系统的整体架构图
  • 旧系统的运行流程图 vs 新系统的运行流程图
  • 分模块逐一拆解:先画出重构前的样子,再画出重构后的样子
  • 新增功能有哪些,怎么在重构后的系统里添加
  • 考虑对旧系统的影响:兼容性问题
  • 考虑实施可行性: 重构成本和收益之间如何权衡

4、重构的具体执行步骤

总体上有两种方式:

  • 新建分支(不推荐)
  • 新建目录(推荐),这样能保证原来旧的那套代码能用。这里又有两种方式:
  • 做减法(先跑通旧的代码,然后一点点删除冗余代码,或重构具体子模块)--推荐,因为如果实在没有开发时间了,某些未完成的模块还可以沿用以前的老代码。
  • 做加法(从每个子功能开始重构,相当于重新构建这套软件系统)-- 不推荐,因为时间成本不可控

5、总结

用设计原则和架构理论武装自己,阅读优秀的源码验证理论,深入理解具体业务,方能设计出一套优雅的软件系统。

责任编辑:武晓燕 来源: 今日头条
相关推荐

2021-12-29 07:18:20

重构工具资源

2020-09-15 12:45:48

系统LinuxUnix

2020-12-29 05:33:40

TomcatSpringBoot代码

2023-07-03 07:27:41

进程线程Win32

2023-07-25 15:06:39

2018-11-30 12:48:36

SDS故障硬件

2023-03-05 18:40:39

iptables防火墙软件

2018-05-16 08:58:04

用户画像存储

2023-09-22 17:36:37

2021-01-28 22:31:33

分组密码算法

2020-05-22 08:16:07

PONGPONXG-PON

2020-03-31 10:08:15

零信任安全软件

2020-01-17 09:07:14

分布式系统网络

2020-05-12 22:24:44

JVM系统加载器

2022-08-08 08:25:21

Javajar 文件

2023-05-15 08:38:58

模板方法模式

2021-08-04 09:32:05

Typescript 技巧Partial

2021-01-29 08:32:21

数据结构数组

2019-02-13 14:15:59

Linux版本Fedora

2022-11-01 08:46:20

责任链模式对象
点赞
收藏

51CTO技术栈公众号