重构
参数过长
影响:
方法不易被理解、使用,方法签名容易不稳定,不易维护
解决方法:反复使用提炼方法+内联方法,消除多余参数
尽量把方法移进相关的类中
如实体类中的get方法在其他类中没有被调用可以删除
实际工作中,可以结合参数数量、以及自身对业务的理解,在 最小知道 和 保持对象完整性 之间进行权衡
全局变量
影响:可以在任何位置进行修改,在使用过程中可能出现意想不到的值,并且没有任何机制可以探测出哪段代码进行了修改,导致定位困难
改进目标:
降低代码耦合性,保持代码清晰,维护简单,降低由于对全局数据随意的改变引发bug的风险
方法:封装变量,提炼函数
使用Convert To Instance Method替换项目中使用静态方法的地方,进行重构
如果想避免其他类中引用本类的字段,修饰符改为private
可变数据
影响:
影响可维护性,在一处修改数据,却在另一处造成难以发现的破坏
改进目标:
应用“数据不可变”:不可变性是强大的代码防腐剂
方法:
封装变量、拆分变量、提炼函数、移除设值,函数、查询取代派生、Builder模式创建不可变对象、引用对象改为值对象、函数式编程等
发散式变化
定义:
某个模块经常因为不同的原因在不同的方向上发生变化
影响:
通常,发散式变化是由于多个变化方向之间有较多的来回调用或者函数内部混合了多类处理逻辑。当处于多个不同上下文的外部行为发生变化时候,都会引起对同一个类或模块的修改,影响了代码的可读和可维护性
改进目标:
提高代码组织结构、职责单一提升代码可读性、可维护性
方法:
拆分阶段
搬移函数
提炼函数
提炼类
霰弹式修改
定义:
霰弹式修改:指的是如果每遇到某种变化,你都必须在许多不同的类内做出许多小修改。此时,你所面临的坏味道就是霰弹式修改
影响:
修改的地方四面散布,而且有可能修改之后导致其他异常结果
改进目标:
更好的代码组织,更少的代码重复
方法:
搬移函数、搬移字段、函数组合成类、函数组合成变换、拆分阶段、内联函数、内联类
clone的应用
这里之所以使用 clone,是因为在跨边界调用场景下,直接更改原对象会有一定风险。
比如某些场景下,边界外更改对象可能改变边界内状态,或者调用方想维护多个状态时可能引发混乱。
因此一般涉及状态信息更新时,尽量不直接处理同一对象(使用clone),或不暴露完整对象(返回部分需要的属性)。
如果需要内部维护对象状态,又需要将完整对象暴露出去,就返回一个副本(比如当前案例,如果 clonePaySlip 需要在内部缓存,那么返回值应当进一步优化为:clonePaySlip.clone())
依恋情节
定义:
依恋情节/特性依恋:一个函数跟另一个模块中的函数或数据交流格外频繁,远胜于在自己所处模块内部的交流
影响:
可读性、可维护性低:调用另一模块功能时往往需要打一套组合拳才能完成,需要知道过多的细节;往往会伴随有“内幕交易、重复代码、霰弹式修改……”
改进目标
将函数搬移到对应的类,解除跨模块的过多交流
方法
提炼函数、搬移函数
注:策略模式、访问者模式往往会带来依恋情节,这不是说这两个模式不可取。我们需要理解:
从根本上来说,我们消除“依恋情节”和应用这些设计模式都是为了把一起变化的东西放到一块儿
——《重构,改善既有代码的设计》
数据泥团
定义
总是成块出现的相同数据项,包括多个类中相同的字段、多个方法签名中相同的参数等
影响
成块出现的重复参数过多,影响阅读和理解,难维护
改进目标
减少相同的字段及入参,缩短入参列,简化函数调用
方法
提炼类 引入参数对象 保持对象完整性
Tip:内联
这里之所以能够直接内联,是因为字段是final的,只会读取,不会更新,大家可以尝试在非final上内联,IDEA会提示我们内联失败。因此当我们需要通过内联的方法消除字段时,需要先想办法把字段变为final的
Ctrl+Alt+N