把书读薄 | 《设计模式之美》规范与重构(上)(二)

简介: 本文是 规范与重构 (15-33) 的浓缩总结,同上,把实战部分(34-37) 拆到下节,这部分主要是一些编码建议和规范,过一遍,自己写代码注意下就好,比较简单。 二手知识加工难免有所纰漏,感兴趣有时间的可自行查阅原文,谢谢。

测试结果如下:


网络异常,图片无法展示
|


从上面的示例我们可以总结出:写单元测试就是针对代码设计覆盖各种输入、异常、边界条件的测试用例,用将用例翻译成代码的过程。


另外,翻译代码时,可利用单元测试框架(如JUnit、TESTNGINX、Spring Test等) 来简化测试代码的额编写。


③ 为什么要写单元测试


  • 帮你发现代码中的BUG (节省fix低级bug的时间,写出Bug Free代码是判断工程师编码能力的重要标准之一);


  • 帮你发现代码设计上的问题 (代码的可测试性是评判代码质量的重要标准,难写单元测试一般说明代码设计可能有问题);


  • 单元测试是对集成测试的有力补充 (复杂系统,集成测试也无法覆盖得很全面);


  • 写单元测试的过程本身就是代码重构的过程


  • 阅读单元测试能帮助你快速熟悉代码(单元测试案例实际上就是用户案例,反映了代码的功能及使用,在没有文档或注释的情况下,它可以起替代性作用,借助单元测试案例,无需深入阅读代码,即可了解代码实现了什么功能);


  • 单元测试是TDD可落地执行的改进方案 (Test Driven Development,测试驱动开发,测试用例优于代码编写);


④ 编写单元测试的经验总结


写单元测试真的是件耗时的事吗?


代码量多,写的过程繁琐,但并不是很耗时,因为不用考虑太多代码设计上的问题,大部分是cv操作。


对单元测试的代码质量有什么要求吗?


单元测试毕竟不会在生产环境运行,类的测试代码都相对独立,代码质量要求可以放低些,命名不是很规范、代码重复有些重复,也是可以的。


单元测试只要覆盖率高就够了吗?


测试覆盖率是比较容易量化的指标,常常作为单元测试写得好坏的评判标准。有很多现成的工具专门用来做覆盖率统计(如JaCoCo、Cobertura等)。覆盖率的计算方式也有很多种,最简单的语句覆盖、稍微高级点的:条件覆盖、判定覆盖、路径覆盖。


盲目追求高覆盖率是不可取的,更重要的是看测试用例是否覆盖了所有可能的情况,特别是一些边界条件。


过度关注覆盖率会导致开发人员为了提高覆盖率,写了很多没必要的测试代码(如get、set)。从过往经验来讲,一个项目的单元测试覆盖率在60~70%即可上线,当然如果项目对代码质量要求严格,亦可适当提高覆盖率要求。


写单元测试需要了解代码的实现逻辑吗?


不需要,单元测试只关心被测函数实现了什么功能,切不可为了追求覆盖率,逐行阅读代码,然后针对实现逻辑编写单元测试。


如何选择单元测试框架?


团队内部统一即可,自己写的代码用已选定的单元测试框架无法测试,多半是代码写的不够好,可测试性差,这个时候要重构自己的代码,使其更易测试,而不是找另一个更加高级的单元测试框架。


单元测试为何难落地执行?


  • 写单元测试本身较繁琐,技术挑战不大,很多程序员不愿意去写;
  • 国内研发比较偏向"快、糙、猛",容易因为开发进度紧,导致单元测试执行的虎头蛇尾;
  • 团队成员没有建立对单元测试的正确认识,觉得可有可无,单靠督促很难执行得很好;


0x3、代码的可测试性


代码的可测试性,粗略地讲就是:针对代码编写单元测试的难易程度,对于一段代码很难为其编写单元测试,或者写起来很费劲,需要依赖单元测试框架中很高级的特性,那么往往意味着代码设计不够合理,代码的可测试性不高。


如果代码中依赖了外部系统或不可控组件(如数据库、网络通信、文件系统等),就需要将被测代码与外部系统解依赖,这种解依赖的方法称作 "Mock",即用一个 "假" 的服务替代真正的服务,Mock服务在我们的控制下,模拟输出我们想要的数据。Mock方式又分两种,手动和使用框架Mock。


常见的测试不友好的代码有这几种


  • 代码中包含未决行为逻辑 (输入随机或不确定,如时间、随机数相关代码,应将其抽取到到方法中,方便测试替换)
  • 滥用可变全局变量 (测试用例可能相互影响);
  • 滥用静态方法 (同上,还有静态方法可能不好Mock)
  • 使用复杂的继承关系 (父类需要mock某个依赖对象才能进行单元测试,所有子类都要mock这个依赖对象);
  • 高度耦合的代码 (一个类要依赖十几个外部对象才能完成工作,单元测试时可能要mock这十几个依赖的对象);


0x4、如何通过封装、抽象、模块化、中间层等解耦代码


① 解耦为何如此重要?


  • 重构 是保证 代码质量 不至于腐化到无可救药的有效手段;


  • 解耦 则是对代码重构,保证代码不至于 复杂 到无法控制的有效手段;


"高内聚、低耦合" 的特性可以让我们聚焦在某一模块或类中,而不需要了解太多其他模块或类的代码,让焦点不过于分散,降低阅读和修改代码的难度。依赖关系简单,耦合小,修改代码也不至于牵一发而动全身,代码改动集中,引入bug的风险也少了,而且可测试性更佳,容易Mock或只需Mock少量外部依赖。


② 如何判断代码是否需要解耦


除了看改代码会不会牵一发动全身外,还可以把 模块间、类与类间的依赖关系画出来,根据依赖关系图的复杂性来判断是否需要解耦重构。如果依赖关系复杂混乱,那从代码结构上讲,可读性和可维护性肯定不是太好。


③ 如何给代码解耦?


  • 封装与抽象 (隐藏复杂性,隔离变化,对外提供稳定易用接口);


  • 中间层 (简化模块或类之间的依赖关系);


  • 模块化 (模块只提供封装了内部实现细节的借口给其它模块使用,以此减少不同模块间的耦合度);


  • 其他设计思想和原则 (单一职责、基于接口而非实现编程、依赖注入、多用组合少用继承,迪米特法则);
相关文章
|
25天前
|
设计模式 Java API
重构旧代码的秘诀:用设计模式 - 适配器模式(Adapter)给Java项目带来新生
【4月更文挑战第7天】适配器模式是解决接口不兼容问题的结构型设计模式,通过引入适配器类实现目标接口并持有不兼容类引用,实现旧代码与新接口的协作。适用于处理兼容性问题、整合遗留代码和集成第三方库。应用时,识别不兼容接口,创建适配器类转换方法调用,然后替换原有引用。注意保持适配器简单、使用组合和考虑扩展性。过度使用可能导致系统复杂和维护成本增加,应谨慎使用。
|
7月前
|
设计模式 算法 Java
设计模式第十五讲:重构 - 改善既有代码的设计(下)
设计模式第十五讲:重构 - 改善既有代码的设计
239 0
|
7月前
|
设计模式 Java 测试技术
设计模式第十五讲:重构 - 改善既有代码的设计(上)
设计模式第十五讲:重构 - 改善既有代码的设计
260 0
|
7月前
|
设计模式 Java Apache
设计模式第九讲:常见重构技巧 - 去除不必要的!=
设计模式第九讲:常见重构技巧 - 去除不必要的!=
|
7月前
|
设计模式 算法 Java
设计模式第八讲:常见重构技巧 - 去除多余的if else
设计模式第八讲:常见重构技巧 - 去除多余的if else
|
11月前
|
设计模式 SQL 算法
【Java设计模式 规范与重构】 六 代码重构小结
【Java设计模式 规范与重构】 六 代码重构小结
145 0
|
11月前
|
设计模式 前端开发 Java
【Java设计模式 思想原则重构】设计思想、设计原则、重构总结
【Java设计模式 思想原则重构】设计思想、设计原则、重构总结
142 0
|
11月前
|
设计模式 存储 Java
【Java设计模式 规范与重构】 五 重构实战:基于ID生成器case(下)
【Java设计模式 规范与重构】 五 重构实战:基于ID生成器case(下)
174 0
|
11月前
|
设计模式 存储 SQL
【Java设计模式 规范与重构】 五 重构实战:基于ID生成器case(上)
【Java设计模式 规范与重构】 五 重构实战:基于ID生成器case(上)
103 0
|
11月前
|
设计模式 IDE Java
【Java设计模式 规范与重构】 四 小型重构的手段:规范的十五条军规
【Java设计模式 规范与重构】 四 小型重构的手段:规范的十五条军规
97 0