2 某个类做了应该有两个类做的事
3
4
①确定分出去的部分要做什么事情 ②创建一个新的类,表示从旧地方分离出来的责任 ③旧类创建时,为新类初始化 ④使用搬移函数手法将需要的方法搬移到新的类(搬移函数时候就将调用地方改名) ⑤删除多余的接口函数,并为新类的接口取一个适合自己的名字 ⑥考虑是否将新的类开放为公共类 | 单一职责 | 内联类 | Inline Class | 1 一个曾经有很多功能的类,在重构过程中,已经变成一个毫无单独职责的类 2 某个类没有做太多事情 3需要对两个类重新进行职责划分 | ①将需要内联的类中的所有对外可调用函数(也可能是字段)在目标类中新建一个对应的中间代理函数 ②修改调用者,调用代理方法并测试 ③将原函数中的相关方法(字段)搬移到新地方并测试 ④原类变为空壳后就可以删除了 | 内聚 |
| | | | |
隐藏委托关系 | Hide Delegate | 1 一个类需要隐藏其背后的类的方法或事件 2 一个客户端调用类的方法时候,必须知道隐藏在后面的委托关系才能调用 | 【重复】{ ①在服务类(对外的类)中新建一个委托函数,让其调用受托类(背后的类)的相关方法 ②修改所有客户端调用为这个委托函数 } 直到受托类全部被搬移完毕,移除服务类中返回受托类的函数 | 解耦 即使将来委托关系发生变化,变化也只会影响服务对象,而不会直接波及所有客户端。 |
移除中间人 | Remove Middle Man | 1 因为隐藏委托关系(当初可能是比较适合隐藏的)手法造成的现在转发函数越来越多 2 过度的迪米特法则造成的转发函数越来越多 | ①在服务类(对外)内为受托对象(背后的类)创建一个返回整个委托对象的函数 ②客户端的调用转为连续的访问函数进行调用 ③删除原本的中间代理函数
这能通过可自动化的重构手法来完成,你可以先对受托字段使用封装变量,再应用内联函数内联所有使用它的函数 | AA |
| | | | |
搬移函数 | Move Function | 1 随着对项目(模块)的认知过程中,也可能是改造过程中,一些函数已经脱离了当前模块的范围 2 一个模块内的一些函数频繁的与其他模块交互,却很少和自身内部进行交互(出现了叛变者) 3 一个函数在发展过程中,现在他已经有了更通用的场景 | ①查找要搬移的函数在当前上下文中引用的所有元素(先将依赖最少的元素进行搬离) ②考虑待搬移函数是否具有多态性(复写了超类的函数或者被子类重写) ③复制函数到目标上下文,调整函数,适应新的上下文 ④函数内使用的变量考虑是一起搬移还是以参数传递 ⑤改写原函数为代理函数(也可以内联) ⑥检查新函数是否可以继续进行搬离 | 内聚 |
搬移字段 | Move Field | 1 随着业务推进过程中,原有的数据结构已经不能很好的表示程序的逻辑 2 每当调用一个函数时,需要传入的记录参数,总是需要传入另一条记录或者他的某些字段一起 3 修改(行为)一条记录时,总是需要同时改动其他记录 4 更新(数据)一条字段时,总是需要同时在多个结构中作出修改 | ①源字段已经被封装(如果未封装,则应该先使用封装变量手法对其封装) ②目标对象上创建一个字段,及其访问函数 ③源对象对目标对象的字段做对应的代理 ④调整源对象的访问函数,令其使用目标对象的字段 ⑤测试 ⑥移除源对象的字段 ⑦视情况而定决定是否需要内联变量访问函数 | 封装 |
| | | | |
搬移语句到函数 | AA | 1 重复代码 2 每次调用a方法时,b操作也总是每次都执行 3 <某些语句放在特定函数内更像一个整体 | ①将重复代码使用搬移函数手法到紧邻目标函数的位置 ②如果目标函数紧被唯一一个原函数调用,则只需要将原函数的重复片段粘贴到目标函数即可 ③选择一个调用点进行提炼函数,将目标语句函数与语句提炼成一个新的函数 ④修改函数其他调用点,令他们调用新提炼的函数 ⑤调整函数的引用点 ⑥用内联函数手法将目标函数内联到新函数里 ⑦移除原目标函数 ⑧对新函数应用函数改名手法(改变函数声明的简单做法) | 提炼 |
函数搬移到调用者 | AA | 1 随着系统前进过程中,函数某一块的作用发生改变,不再适合原函数位置 2 之前在多个地方表现一致的行为,如今在不同调用点面前表现了不同的行为
本手法只适合边界有些许偏移的场景,不适合相差较大的场景 | ①简单情况下,直接剪切 ②将不想搬移的部分提炼成与当前函数同级函数(如果是超类方法,子类也要一起提炼) ③原函数调用新的同级函数 ④替换调用点为新的同级函数和要内联的语句 ⑤删除原函数 ⑥使用函数改名手法(改变函数声明的简单做法)改回名字 | AA |
| | | | |
以函数调用替换内联代码 | AA | 1 函数内做的某些事情与已有函数重复 2 已有函数与函数之间希望同步变更 | ①内联代码替换为函数(可能有参数,就要对应传递) | 封装 |
替换算法 | AA | 1 旧算法已经不满足当前功能 2 有更好的方式可以完成与旧算法相同的事情(通常是因为优化) | ①保证待替换的算法为单独的封装,否则先将其封装 ②准备好更好的算法 ③替换算法 ④运行并测试新算法与旧算法对比(一定要对比,也许你选的还不如以前呢) | 策略 |
| | | | |
字段改名 | AA | 1 记录结构中的字段需要改个名字 | 如果结构简单,可以一次性替换 ①如果记录没有封装,最好是先封装记录 ②修改构造时候做兼容判断(老的值与新的值兼容判断:this.a = data.a OR data.b) ③修改内部设取值函数 ④修改记录数据类中的内部调用 ⑤测试 ⑥修改外部调用初始化时候的数据 ⑦删除初始化兼容判断 ⑧使用函数改名手法(改变函数声明的简单做法),修改调用处的调用方式及内部取设值函数为新字段名 | AA |
以查询取代派生变量 | AA | 计算的参考变量,是不可变的,计算结果也是不可变的。可以不重构(还是那句话,不可变的数据,我们就没必要理他) 1 两个变量相互耦合 2 设置一个变量的同时,将另一个变量与该变量结合,通过计算后给另一个变量设置值 | ①确定可以引起变量发生变化的所有点(如果有来自其他模块变量,需要先用拆分变量手法) ②新建一个计算函数,计算变量值 ③引入断言(assert),确保计算函数的值与该变量结果相同 ④测试 ⑤修改读取变量的代码,用内联函数手法将计算函数内联进来) ⑥用移除死代码手法将旧的更新点的地方清理掉 | AA |
| | | | |
将引用对象改为值对象 | AA | 1 几个对象中共享了一个对象,并且要联动变更的情况下 2 值对象就是每次设置都直接设置这个值,比如: 值对象:a.b=new b(1); 引用对象:a.b.c=1 | ①检查重构的目标是否为不可变对象,如果不是的话,则看看是否可以将其改为不可变对象 【重复】{ ②用移除设值函数手法去掉第一个设引用值函数(每次都用设置值的方式复写整个对象) ③测试 } ④判断两次相同输入时候,值是否相等 | 不可变数据 |
将值对象改为引用对象 | AA | 1 数据副本在多处使用,并且需要一处变化其他地方同步更新 | ①创建一个仓库(如果没有的话),仓库要支持:每次访问相同数据都是一个相同的引用对象、支持注册新数据和获取同一个引用数据 ②确保仓库的构造函数有办法找到关联对象的正确实例 ③修改调用点,令其从仓库获取关联对象。 ④测试 | 可变数据 |
| | | | |
AA | AA | 1 2 3 4 | ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ | AA |
AA | AA | 1 2 3 4 | ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ | AA |
处理概括关系
名称 | 定义 | 用法 |
字段上移(Pull Up Field) | 两个子类拥有相同的字段。 | 将该字段移至超类。 |
函数上移(Pull Up Method) | 有些函数,在各个子类中产生完全相同的结果。 | 将该函数移至超类。 |
构造函数本体上移(Pull Up ConstructorBody) | 你在各个子类中拥有一些构造函数,他们的本体几乎完全一致。 | 在超类中新建一个构造函数,并在子类构造函数中调用它。 |
函数下移(Push Down Method) | 超类中的某个函数只与部分(而非全部)子类有关。 | 将这个函数移到相关的那些子类去。 |
字段下移(Push Down Field) | 超类中的某个字段只被部分(而非全部)子类用到。 | 将这个字段移到需要它的那些子类去。 |
提炼子类(ExtractSubclass) | 类中的某些特性只被某些(而非全部)实例用到。 | 新建一个子类,将上面所说的那一部分特性移到子类中。 |
提炼超类(Extract Superclass) | 两个类有相似特性。 | 为这两个类建立一个超类,将相同特性移至超类。 |
提炼接口(ExtractInterface) | 若干客户使用类接口中的同一子集,或者两个类的接口有部分相同。 | 将相同的子集提炼到一个独立接口中。 |
折叠继承体系(Collapse Hierarchy) | 超类和子类之间无太大差别。 | 将它们合为一体。 |
塑造模板函数(Form TemPlateMethod) | 你有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上所有不同。 | 将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。 然后将原函数上移至超类。 |
以委托取代继承(Replace Inheritance withDelegation) | 某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。 | 在子类中新建一个字段用以保存超类;调整子类函数令它改而委托超类;然后去掉两者之间的继承关系。 |
以继承取代委托(Replace Delegation withInheritance) | 你在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数。 | 让委托类来继承受托类。 |
14.大型重构** | | |