2023年阅读笔记7

发布时间 2024-01-09 16:11:20作者: wardream

《修改代码的艺术》

遗留代码工作的三个关键概念:感知、分离和接缝 。

感知和分析和解依赖直接相关,解依赖是将类放入测试用具的重要手段( 有时是唯一手段) ,因为类之间往往是相互依赖,相互影响的,为了能单独测试某类,我们需要接触类之间的依赖关系,尤其是测试类所依赖的类。很多时候解依赖唯一的办法就是通过伪装成被影响的类来直接感知所受到的影响。感知和分离式解依赖的两个目的:1) 感知;当无法感知代码的状态 ( 测试) 时,通过解依赖来感知“状态”;2) 分离:当无法将代码放入测试用具时,通过解依赖来分离测试代码。

类难于测试的根本原因在于:类很少是单独存在的,往往是相互依赖。所要测试的类往往存在如下依赖关系:1) 实例对象(成员变量); 2) 委托对象(接口参数); 3) 临时对象(所创建或实例化的任何对象); 4) 全局对象(单体,系统或库API )。

注:上述对象不包括基本类型和基本对象(如STL 等标准库)。

所有的对象都存在自己的逻辑,从而如果想要编写单独的类测试用例就应该接触对这些对象的依赖。下面一一给出解决方案。

1) 实例对象(成员变量):使用伪对象

2) 委托对象(接口参数):使用伪对象

3) 临时对象(所创建或实例化的任何对象):使用伪对象

4) 全局对象(单体,系统或库API ) :以获取方法替换全局引用;封装API ,形成内部调用接口,通过子类化并重写方法,屏蔽不必要的系统API 调用。

 

使用伪对象的解依赖技术:

参数化构造函数:直接传入伪对象。

参数化方法:避免临时对象的硬编码,直接传入伪对象

引入实例委托:避免全局对象的硬编码,直接传入伪对象

替换实例变量:增加实例变量设置接口,不推荐。

引入静态设置方法:用于替换静态对象, 如单体实例。

参数适配:将依赖的参数类型替换成可伪装的自定义类型。

实现提取:用于提取抽象基类或接口 ,进而定义伪对象Fake

接口提取:用于提取抽象基类或接口,进而定义伪对象

 

接触类内部依赖的解依赖技术:

子类化并重写方法 :将少数无须测试的内部接口重写,通过实例化重写的派生类来测试原有类的接口。这里的重写是指虚函数的重实现,而非覆盖。

提取并重写调用:封装API ,并重写该接口

提取并重写工厂方法:用于实例化伪对象,避免更改接口,本质就是替换实例变量。

提取并重写获取方法:延迟获取实例变量,用于C++ 。因为C++ 在构造函数中虚函数机制被禁止,故无法再构造函数中调用子类重写的工厂方法。

 

特定情况下的解依赖技术:

分解出方法对象:重构巨型方法

朴素化参数:避免参数依赖

封装全局引用:

以获取方法替换全局引用:利于重写获取方法。

定义补全:C/C++ 的定义和实现是分开的,通过重写实现替换原有行为。

连接替换:利用链接期接缝,替换库,DLL 等,实现行为替换。

暴露静态方法:避免对象的实例化,用于难实例化的对象。

换函数为函数指针:用于C 的行为替换,不推荐。