一、概述
重构三部曲为:思想准备,单元测试,重构
- 思想准备的目的是明确:为什么要重构,重构的理论支撑是什么
- 单元测试:重构依托于良好的测试,单元测试是保证重构顺利进行的神兵利器
- 重构:依照《重构》中的重构技巧进行实战
本文主要是摘录自《重构 改善既有代码设计》的原话,有一部分是个人对原文的个人理解。
重构的思想并不是只适用于重构,也适用于开发过程中的代码编写,两者的目的都是写出良好的代码。
光知道还不够,还必须付诸应用;光有决心还不够,还必须行动
二、代码需要意图明显
- 重构的定义:在不改变软件可观察行为的前提下改善其内部结构。
- 重构的意义在于:你永远不必说对不起--只要把出问题的地方修补好就行了。(“封装”不可能永远都是适用的,可能过一段时间之前很完美的封装就会显得很“臃肿”了)
- 你的代码首先是为人写的,其次才是为计算机写的,写出人类容易理解的代码
- “封装”意味着每个对象都应该尽可能少了解系统的其他部分
- 关键不在于函数的长度,而在于函数“做什么”和“如何做”之间的语义距离
- 代码用途和实现手法之间的语义距离
- 意图要明确,明确说出我所需要的。
- 提高代码的可理解性,降低其修改成本
- 避免“代码传达的信息与你的意图南辕北辙”
- 需要为代码编写文档意味着代码本身写的不好
- 函数的名称应该准确表达它的用途
- 好的代码,更有利于性能的优化(不要因为性能而牺牲代码的清晰性)
三、重构要小步前进
- 重构依托于良好的单元测试。
- “重构需要一组可靠的测试环境”
- 测试应该是一种风险驱动行为
- 一定要快速前进,快速失败并更正,然后再重复
- 重构应该小步前进,比如修改一个局部变量或者提炼一个函数
- 每一步出现异常都应该回滚上个版本,保证输出不变
- 设计模式为重构提供了目标
- 你之所以进行重构,必定是为了达到某个目的,而不仅仅是为了看起来有所动作
- 大型重构耗费是假长,在这个过程中,你应该根据需要安排自己的工作,只在需要添加新功能或修补错误时擦进行重构。重构程度只要能满足其他任务的需要就行了
- 应对并处理变化,是软件开发的根本复杂性之一
- 重构时不应该有任何添加功能的操作,重构是对已有功能的操作,必须保证软件可观察方式(输出)是不变的。
- 哪怕你完全了解系统,也请实际度量它的性能,不要臆测。臆测会让你学到一些东西,但十有八九你是错的。
- 你必须培养出自己的判断力。
- 问题发现的越早,为修复而付出的代价越低。
四、重构方法的一些摘录
- 一个类应该是一个清楚的抽象,处理一些明确的责任
- 决定把责任放在哪儿
- 分解类所负的责任,分解出新类的旧类如果责任与名称不符,进行更名
- 间接性可能带来帮助,但非必要的间接性总是让人不舒服
- 观察类应该做的所有事情,然后针对任何一项功能的任何一种可能失败情况
- 增加子类的目的,是为了增加新特性或变化其行为
- 如果某个类在不同环境下扮演截然不同的角色,使用接口就是个好主意
- 绝大多数情况下,函数应该放在它所使用的数据的所属对象内
- 将复杂的处理过程分解成小函数
- 先搬离低层函数,再搬离高层函数
- 将查询函数和修改函数分离
- 所谓重构继承体系,往往是将函数和字段在体系上下移动
- 如果你手上没有所需的东西,总可以叫另一个对象给你
- 在分布式软件中,函数的往返必须被减至最低限度
- 任何有返回值的函数,都不应该有看得到的副作用
-
所有的数据都应该隐藏起来
- 数据隐藏,你绝不应该将数据声明为public
- 哑数据对象--除数据访问函数外,没有其它任何函数
- 程序当中,复杂的条件逻辑是最常导致复杂度上升的地点之一
- ”分支逻辑“和”操作细节“分开
- 加入断言,永远不会影响程序的行为。
- 不要滥用断言
- 更多的时候,断言的价值在于:帮助程序员理解代码正确运行的必要条件
- 过长的参数列总是难以理解的
- 如果有必要,将参数的计算过程提炼到一个独立函数中
- 应该只在必要关头才添加参数,预先添加的参数很可能并不是你所需要的。
- 重复代码是系统中最糟糕的东西之一
- 避免行为重复,因为这容易导致 ”修改一个却未能修改另一个“的风险