风行的博客

代码重构及代码Review

代码重构 - 重构就是在不改变软件系统外部行为的前提下,改善它的内部结构。

Martin Flower 在《重构》中有一句经典的话:"任何一个傻瓜都能写出计算机可以理解的程序,只有写出人类容易理解的程序才是优秀的程序员。"

重构原则

随着时间推移,需求的频繁变化等原因都会导致代码质量逐步下降,所以持续重构就变得越来越重要,并且重构对开发人员技术水平及经验的要求会更高一些,下面列出几条重构原则:

  • 要充分理解旧代码后再进行修改,模块之间过度耦合导致牵一发而动全身,不易控制影响范围,有时代码摆放顺序的修改都会造成问题

  • 优先重构经常修改的部分,如果代码一两年都没有修改过,那么说明改动的收益很小

  • 重构可能需要很长时间,并且不是非要一次做完,主要取决于团队对于风险的容忍程度

  • 删除无用代码是提高代码可维护性最有效的方式

  • 小范围重构,当重构后会导致大量测试工作时,需要谨慎决定是否有必要重构

  • 要构建可扩展系统,我们需要认同错误的不可避免性,有时候避免这些错误会带来高昂的成本,因此我们不妨将注意力集中在对问题的快速检测以及出现后的应对措施上


哪种代码需要重构

  • 臃肿的类:一个类应该只做一件事,可通过一些设计模式去拆分

  • 长方法:方法应尽量短小,专注一个功能点,并且要放在合适的类里

  • 重复代码:往往由于开发人员对框架不熟悉,也有个别不讲究的情况

  • 方法有多个参数:可通过提取成类对象或字典解决,个别情况也可拆分方法

  • 魔鬼数字:尽量用常量去定义数字,或在使用处添加注释说明用意

  • 模糊的命名:要做到见名知意

  • if/else 嵌套过多:尽量不超过3层,如果不能简化逻辑,可通过拆分方法或添加注释方式解决


Xcode 提供的重构功能

跟别的开发工具相比 Xcode 提供的重构功能简直弱暴了,不过有总比没有好,Xcode 提供了以下几个重构功能,从菜单栏中进入:Edit -> Refactor,或在代码上右键,然后选择 Refactor

  • Rename:重命名类、方法、变量
  • Extract:将方法中的一段代码抽取为一个独立的方法
  • Create Superclass:给当前选中的类创建父类
  • Move Up:将选中方法或属性移到父类中
  • Move Down:将父类中选中的属性移到子类中
  • Encapsulate:封装,生成 setter/getter,一般用不上

相对来说,Rename 更常用一些,有时也会用到 Extract,最后吐槽一下

  • Extract:使用时 Xcode 经常崩掉
  • Rename:并不能保证全部覆盖到,还需要自己再次检查


代码 Review

对于代码质量更多时候还是需要通过持续的代码 Review 来保证,需要做到以下几点:

  • 要有一个 CheckList,对一些代码问题进行定义,要求开发人员以此为准

  • 对于代码规范上的问题应该通过自动 Review 工具去做,如 OCLint/SwiftLint

  • 对于代码逻辑及性能上的问题可通过 pull/merge request 方式去做

Review 的目的不仅是要保证代码质量,提高团队开发人员技术水平也是一个重要目的。

CheckList

  • 代码简洁不重复
  • 方法短小并专注一个功能
  • 删除无用的代码及资源
  • 适时地使用 private 实现封装并使代码更清晰
  • 避免无意义的 log,保持好的编码习惯
  • 不要用魔术数字
  • 框架代码及复杂代码要写注释
  • 遍历数组或字典时不允许添加或删除元素
  • 代码尽量不要有警告
  • 相似意义的常量超过 2 个时就要考虑用枚举代替
  • 命名要做到“见名知意”,UI 控件的命名要能看出来是什么控件
  • 使用 xib 或 storyboard 画界面来降低代码量,UI 控件可通过纯代码实现
  • 尽量避免循环引用问题
  • 避免对可选类型强解包,可用 if let foo = foo { ... } 或可选链 foo?.doSomething()
  • 尽早的退出方法,可提升代码的可读性,例:guard xx else { return }if xxx { return}
  • 定义 model 用 struct,值类型是线程安全的,并以栈的形式分配,速度上比 class 快
  • 用于对接接口返回数据的 model 中的属性必须是 optional 的,当接口返回字段较少时,可用字典
  • 不要在 ViewModel 里用到跟 UIKit 相关的东西,Controller 当 View 用,尽量不写逻辑性代码
  • UI 切的图需要压缩后再使用,图片命名方式: core_hud_loading@2x.png
  • 项目的主色和辅色有对应常量,如果在 xib 中使用,需要先将做好的 .clr 文件复制到指定目录
  • // MARK:来做代码分段,用// TODO:来做待办,但需要写出计划完成时间或条件
  • 尽量多用常量来增强代码的不可变性,可以让代码更加安全并提高可读性
  • 尽量避免混合使用 Swift 类型和 NSObject 子类,这样会造成大量的类型转换,对性能有影响