修改代码的艺术

思考区

遗留代码的修改方案

我们从系统思维的角度来看一下遗留代码的修改方案。
首先是遗留代码容易出现的问题。
任何代码的修改都需要基于一个原则,那就是保证现有功能的不变。
这是一个难点,越是大的系统,修改起来就越难,由此,开发团队在应对代码修改时往往会选择保守,能不动就不动,这又进而导致第二个问题,代码出现冗余。
基于系统的增强回路原理,冗余越多,冗余的增加量就越多,再加上复杂系统具有反馈延迟的特性,等开发团队意识到代码已经难以维护的时候,一切就晚了。这个时候常见的手段就是引入一个新技术,借此推倒重来了。
其次,关于修改代码的方案,从系统思维的角度来看。
据系统组成:元素、关系、功能
为了保证现有功能的不变,我们需要引入单元测试。由此,这个系统最终包含两方面的元素,一是被改动的代码,二是被添加的单元测试。
而他们之间的关系,即功能代码与功能代码之间,功能代码与测试代码之间的关系,则需要考量,或者我们用软件开发中的术语-依赖。
那么首先要解决的问题就是确定改动哪里和测试哪里。
然后确定他们之间的依赖。
再根据测试驱动开发(TDD)的原则,我们需要先编写测试。
因此,修改代码的流程如下

  1. 确定改动点
  2. 找出测试点
  3. 解依赖
  4. 编写测试
  5. 修改重构

笔记区

原则&工具

一、为什么要修改代码

  1. 添加特性
  2. 修复bug
  3. 改善设计(重构:在不改变行为的前提下,改善设计)
  4. 优化(相对与重构,可能会改变资源(时间或空间))

难点1:如何保留既有行为不变
进而导致难点2:团队内部倾向于保守,能不动就不动,再进而导致未来技术的债务。
最后,有了新技术,推倒重来。(复杂系统的反馈延迟)

系统修改的基本方法

  1. 编辑并祈祷(通常做法,非常赞同)
  2. 覆盖并修改

关于验证

传统验证方式,回归测试。
缺陷:

  1. 错误定位难,位于应用层,不够细;
  2. 时间长,依赖测试人员,反馈慢;
  3. 覆盖难以保证。

解决方案:单元测试

单元测试

注:有外部依赖的不是单元测试,数据库、文件系统、网络、环境。
好处:运行快,定位精确。

如何修改

  1. 确定改动点(参考16、17)
  2. 找出测试点(11、12)
  3. 解依赖(25+6+10)
  4. 编写测试 13 8
  5. 修改重构 20 22 21

第3章 感知和分离(Mock)

测试的前提-解依赖
为什么解依赖:

  1. 感知:无法轻易访问代码计算出的值:如依赖数据库和网络
  2. 分离:无法测试的代码块,如私有的分支

手法:伪装成合作者

伪对象(手动实现一个假类)。
仿对象(通过mock工具)。

第4 章 接缝模型(Mock method)

类似Java中Mock方法的概念,作者书中用的c++,没有类似Java中的mock测试框架,所以做法相对复杂。

第5章 工具

一些ide和测试框架之类的介绍,略。

第二部分 修改代码的技术

第6章时间紧迫,但必须修改
第7章漫长的修改
第8章添加特性
第9章无法将类放入测试用具中
第10章无法在测试用具中运行方法
第1l章修改时应当测试哪些方法
第12章在同一地进行多处修改,是否应该将相关的所有类都解依赖
第13章修改时应该怎样写测试
第14章棘手的库依赖问题
第15章到处都是AP调用
第16章对代码的理解不足
第17章应用毫无结构可言
第18章测试代码碍手碍脚
第19章对非面向对象的项目,如何安全地对它进行修改
第20章处理大类
第21章需要修改大量相同的代码
第22章要修改一个巨型方法,却没法为它编写测试
第23章降低修改的风险
第24章当你感到绝垫时

第6章时间紧迫,但必须修改

  • 新生方法
  • 新生类
  • 外覆盖方法
  • 外覆盖类:装饰模式

第7章漫长的修改

第8章添加特性

TDD:

  1. 编写无法编译通过的测试
  2. 实现空类/方法,让编译通过
  3. 实现功能让测试通过
  4. 消除重复代码
  5. 重复步骤
Comments
Write a Comment