抽丝剥茧 - 实例简析重构代码的三板斧

I. 太阳之下无新事:如何面对既有代码

既有代码

可以说,任何软件系统从设计部署好的第二天起,就都变成了既有代码(existing code)。一个几年的系统和一个几周的系统中存在的问题,并无本质上的差异。

俗话说,创业容易守业难。比新搭建一个系统更常见的工作,正是对既有系统的日常维护,一般包括:

  • 添加新特性

  • 修正bug

  • 优化设计和性能

如何有效进行这些工作,而不是陷入“修复一个bug,还你几个新bug”的恶性循环,是每个开发者必然面对的课题。

遗留代码

既有代码的一种极端情况,就是遗留代码(legacy code),一般指那些无人再维护的,或架构非常过时,亦或运行在老旧的操作系统上的代码。

相比于在大抵上每几天就打个照面的既有代码中修修改改;当面对一头雾水的遗留代码时,如果没有正确的方法,即便再小心翼翼,也总会陷入束手无策的境地。

II. 既有代码的困境

每个开发者可能都见过奇奇怪怪的各种具体问题,但归纳起来,主要有这么三种情况:

  • 过重的依赖:实例化或方法调用的过程中,过多过深的依赖于其他类或组件

  • 错误的封装:类或组件承担了不应有或过多的功能

  • 裸奔的功能:没有测试代码或过时、不完整的测试覆盖

III. 重构的意义

面对以上困境,需要做的就是重构:

在不改变代码功能的前提下,改善其设计的行为被称为重构(refactor)

重构的意义:使既有代码更具可维护性,并消除其不确定性

重构的关键:在其过程中不应该有任何功能上的改变

在之前提到过的几种日常工作中,无一例外不需要先进行有效的重构,才能保证工作的顺利进行。

IV. 重构三板斧:解开依赖、合理封装、测试覆盖

同样显而易见的是,将三重困境一一化解,就可以达到理想的重构:

  1. 依赖:

  • 简化导致逻辑复杂的过重依赖,将关注点从散布在艰深冗长的调用链条中拉回来

  • 设置setter方法或借鉴interface的思路等,解决由于关联了太多其他类或组件,从而无法在测试用例中实例化和调用方法的问题

  • 封装:

    • 类或组件只应该承担尽量简单而少量的职责,过长的类或组件应抽取成多个

    • 类或组件不应当包含其子层级或兄弟层级的逻辑

    • 出现在多处的重复代码总是可疑的,应该尽量抽象和提取出来

  • 测试:

    • 测试的作用就是检验正确性和检验变化

    • 回归测试(regression testing):周期性的运行测试,来检验已知的良好行为是否依然正常工作。

    • 单元测试(unit testing):是指对软件中的最小可测试单元进行检查和验证;在 JS 中“单元”一般可以认为是一个函数或一个类。

    • 测试用例(test case):就是设定输入数据,运行被测试函数,然后判断实际输出是否符合预期

    简而言之,前两项是最后“落在实处”的工作,而测试的重要性并不亚于任何工作,甚至是保证前两项进行下去的关键。测试覆盖率(test coverage)越高,说明所覆盖部分的可靠性越有保证,而不必时时担心改动带来的未知影响。

    遵循为独立单元(视情况为函数、类或组件等)编写测试的理念,就可以写出小而易理解的一个个测试用例,也反过来使得代码比写注释更容易理解。

    对于新开发的功能,可以用测试驱动开发(TDD)的方法,即重复“写一点代码->编写测试->失败->修改代码->测试通过”的过程,最终达到方法的完成。

    对于既有代码,可以根据日常需求,对涉及到的部分逐步引入单元测试,持续不断的提高系统的测试覆盖率。

    V. 一个stepper组件的重构实例

    这里举一个足够简单也比较典型的例子:重构stepper组件

    场景描述:

    在这个由 react 组件构建的既有系统中,在若干界面中都引用了一个常见的数字选择器(numeric stepper),其变化会触发判断逻辑,提示商品对应的数量是否有足够库存、是否达到了限购数量等

    既有的结构和问题:

    • “全局”组件NumberStepper里糅杂了具体业务逻辑“限购”和“库存”的判断

    • 以上判断逻辑冒泡到各种容器组件中,“演化”出了同一函数的不同签名形式 -- 梳理后发现:虽然参数定义不同且含糊不清,实际要达成的逻辑却是一样的

    • 相关的逻辑判断代码和弹窗jsx结构,均重复出现于不同组件中

    • NumberStepper和各种容器组件中,均分别存在用 0|1|2|3 定义的判断和显示逻辑,且无注释说明

    问题的分解:

    困境问题A问题B问题C
    依赖过重
    封装错误
    缺乏测试

    问题的解决:

    • 在测试的保护下,将NumberStepper中的具体业务逻辑依赖删除

    • NumberStepper暴露props.checkLimit,在数量增减时响应,将判断逻辑交给调用者

    • 统一判断逻辑onLimitOver的函数签名,明确参数的意义

    • 将展现部分的逻辑和界面分别提取为单一的部分,统一调用

    • 将之前用数字区分的逻辑归纳成常量类,交由各处统一调用

    • NumberStepperOverLimit等组件编写测试,保证改动的全面和正确性

    VI. 总结

    至此,之前的结构得到了有效的梳理;再进行相关的功能添加就有章可循、心中有数了。

    • 处理大部分“添加新特性、修正bug和优化”类的日常工作时,需要科学具体的方法论

    • 过重的依赖、错误的封装、缺少测试,是既有代码的常见问题

    • 有针对性的从以上三个方面入手,并辅以必要的单元测试,就可以保证工作有条理的进行下去

    VII. 参考资料

    • https://book.douban.com/subject/2248759/

    • https://en.wikipedia.org/wiki/Legacy_code

    • https://www.tuicool.com/articles/3QRBRr

    • https://baike.baidu.com/item/单元测试

    (end)

    -------------------------------------

    长按二维码或搜索 fewelife 关注我们哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园建设方案旨在通过融合先进技术,如物联网、大数据、人工智能等,实现校园的智能化管理与服务。政策的推动和技术的成熟为智慧校园的发展提供了基础。该方案强调了数据的重要性,提出通过数据的整合、开放和共享,构建产学研资用联动的服务体系,以促进校园的精细化治理。 智慧校园的核心建设任务包括数据标准体系和应用标准体系的建设,以及信息化安全与等级保护的实施。方案提出了一站式服务大厅和移动校园的概念,通过整合校内外资源,实现资源共享平台和产教融合就业平台的建设。此外,校园大脑的构建是实现智慧校园的关键,它涉及到数据中心化、数据资产化和数据业务化,以数据驱动业务自动化和智能化。 技术应用方面,方案提出了物联网平台、5G网络、人工智能平台等新技术的融合应用,以打造多场景融合的智慧校园大脑。这包括智慧教室、智慧实验室、智慧图书馆、智慧党建等多领域的智能化应用,旨在提升教学、科研、管理和服务的效率和质量。 在实施层面,智慧校园建设需要统筹规划和分步实施,确保项目的可行性和有效性。方案提出了主题梳理、场景梳理和数据梳理的方法,以及现有技术支持和项目分级的考虑,以指导智慧校园的建设。 最后,智慧校园建设的成功依赖于开放、协同和融合的组织建设。通过战略咨询、分步实施、生态建设和短板补充,可以构建符合学校特色的生态链,实现智慧校园的长远发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值