8.1 搬移函数(Move Function)
1 | class Account { |
何时该搬移
- 频繁引用其他上下文中的元素,而对自身上下文中的元素却关心甚少
- 某个函数频繁调用一个别处的函数
- 通用函数,在其他地方也能发挥作用
搬移注意点
- 被调用的函数也需要搬移,从依赖最少的那个函数入手
- 检查待搬移函数是否具备多态性(特别lua这种静态编辑器检查不出来的,先全局搜下调用)
- 减少嵌套函数的使用,很容易在里面编写一些私有数据,并且在函数之间共享
- 迁移涉及传参到新函数时,可以先只传需要的部分,后面拓展起来传对象也行,没必要提前增加用不到的成本(随时可重构)
8.2 搬移字段(Move Field)
1 | class Customer { |
一个适应于问题域的良好数据结构,可以让行为代码变得简单明了,而一个糟糕的数据结构则将招致许多无用代码,这些代码更多是在差劲的数据结构中间纠缠不清,而非为系统实现有用的行为。
坏的数据结构本身也会掩藏程序的真实意图。
何时该搬移
- 数据泥团
- 修改一条记录时,总是需要同时改动另一条记录
搬移注意点
- 先对裸数据进行封装,确认所有修改和读取
- 当有确认不到的数据修改处时,可以加入错误日志,跑一段时间,将错误暴露出来
8.3 & 8.4 搬移语句到函数(Move Statements into Function)、搬移语句到调用者(Move Statements to Callers)
1 | result.push(`<p>title: ${person.photo.title}</p>`); |
何时该搬移到函数
- 消除重复
- 某些语句与一个函数放在一起更像一个整体
何时该搬移到调用者
- 随着系统能力发生演进(通常只要是有用的系统,功能都会演进),原先设定的抽象边界总会悄无声息地发生偏移
- 函数边界发生偏移的一个征兆是,以往在多个地方共用的行为,如今需要在某些调用点面前表现出不同的行为
这个重构手法比较适合处理边界仅有些许偏移的场景,但有时调用点和调用者之间的边界已经相去甚远,此时便只能重新进行设计了
比如处理挂点组件时,一开始以为所有单位骨骼应该是一致的,但随着需求发展,发现原先想的比较理想,这时候就需要重新设计数据结构了
8.5 以函数调用取代内联代码(Replace Inline Code with Function Call)
1 | let appliesToMass = false; |
- 本质也是为了消除重复
- 和项目已有工具函数、库函数搭配使用使本手法效果更佳
8.6 移动语句(Slide Statements)
何时该移动
- 让存在关联的东西一起出现,可以使代码更容易理解
- 取用了同一个数据结构,那么最好是让它们在一起出现
移动注意点
- 把代码移动过去,执行次序的不同会不会使代码之间产生干扰,甚至于改变程序的可观测行为?
比如某个类还没初始化或创建,又或者需要先计算xx才能计算该变量
8.7 拆分循环(Split Loop)
1 | let averageAge = 0; |
- 拆分循环还能让每个循环更容易使用、理解,关心的上下文更少
- 如果一个循环只计算一个值,那么它直接返回该值即可,而不是返回一个数据结构
- 接着还能对拆分得到的循环应用提炼函数(106)
- 关于性能问题:先让代码结构变得清晰,才能做进一步优化
8.8 以管道取代循环(Replace Loop with Pipeline)
1 | const names = []; |
- 更好的语言结构来处理迭代过程,这种结构就叫作集合管道(collection pipeline)
- 一些逻辑如果采用集合管道来编写,代码的可读性会更强,更容易弄清对象在管道中间的变换过程
UniRx/UniTask插件使用了类似的迭代思路
8.9 移除死代码(Remove Dead Code)
- 没用的代码大胆删,有问题有版本控制系统兜底
- 无用代码会带来很多额外的思维负担
- 尝试理解它的工作原理时,却发现无论怎么修改这段代码都无法得到期望的输出。