重构
什么是重构?
问题:比特衰减(Bit rot)
在几个月后或者几个新版本后,许多代码库(databases)达到下面一种状态:
重写:什么都没有从原始代码中留下来
遗弃:原始代码被扔掉了,被从头重写
为什么呢?
系统进化去满足新的需求和添加新的功能
如果代码的结构不也进化,它将会“腐烂”
即使代码起初在核对的时候就被评审并且很好的设计这也有时发生。
代码维护:
维护:软件产品交付后的修改或修复。
目的:修复bugs,提高性能,提高设计,添加功能
有学术表明百分之八十的维护都是因为无关bug(non-bugfix-related)的活动,像是增加功能((Pigosky
1997)
维护很难,维护代码比重新写个新代码更难,难在:
- “纸牌屋“现象(别接触它)
- 必须理解另外一个开发者写的代码,或者是你拥有不同精神状态另外一个时间点写的代码
维护就是开发者如何利用他们的时间,维护要求:
-
致力于良好设计软件和提前计划,为了让之后的维护减少痛苦
-
未来改变的能力必须被意料到
什么是“重构”?
Chikosfky and Cross说:“在同一相对抽象层次上从一个表示转换到另一个表示,同时保留项目系统的外部行为(功能和语义)。
Griswold:“源级结构转换应被保证保证保留程序的含义”
Opdyke:“程序重组保留程序的行为的操作”
重构是改变一个软件系统的过程,它并不改变代码的外部行为但是改善代码的内部结构。
重构是:
-
重组(重排)代码…
-
在一系列小的,保护语义的变形(代码保持工作),为了让代码更容易去维护和修改
重构并不仅仅是任何一个旧的重组
-
你需要保持代码工作
-
你需要一些小的步骤去保护语义
-
你需要有单元测试去保证代码工作
重构改进了软件的非功能性属性,优点包括提升了代码的可读性和降低了复杂性,这些可以提升源代码的可维护性和创建一个更加具有表现力的内部结构或者对象模型去提升可延展性
重构和增加功能,debug代码,重写代码并不是相同的
重构是“行为保护”
一个重构是一个参数化的行为保护程序变形。
行为保护的方法可以静态或动态地执行检查。
为什么重构呢?
为什么修复你系统并不残破的一部分呢?
你系统里的代码地每一部分都有三个目标:
-
执行它的功能
-
允许改变
-
和读它的开发者能好好交流
如果代码做不到上面一个或者更多,它就是残缺的。
代码重构的目的:
-
让它更容易维护
-
让它更容易理解
-
模块化的分解——分解模块(不是局部的)
-
痛苦地并且枯燥地去写(添加修复bug或增添功能的代码)
-
推广或保证程序面向对象
-
设计一致性
-
性能
什么时候去重构呢?
对一个团队来说什么是重构代码的最佳时机呢?作为流程的一部分,最好不断完成(如测试)。这很难在项目后期做得很好(比如测试),或者不如说,最佳时机是任何一次你发现有一个更好的方式去实现的时候。
当你认证你的系统中一个区域如下情况时重构:
-
没有被良好设计
-
没有被严格地测试,但是看上去至今还能工作
-
需要添加新的功能
-
发现一个异味(“bad
smell”)
以下情况,你不需要重构:
-
稳定的代码(不需要更改的代码)
-
别人家的代码(除非你继承它并且它现在是你的了)
例子1:
switch 语句在恰当设计的面向对象的代码中十分罕见
其次,switch语句是一个简单并且很容易被发现的“异味“
当然,不是所有的switch的应用都是糟糕的
一个switch语句不应该被应用在辨别不同种类的目标
针对这种情况有几种被很好设计的重构:
用多态来替换条件句
动机:你有一个一句不同对象类型来选择不同行为的选择句。
技术:移动选择句的每一个分支去一个子类中的一个复写的方法。让原有的方法抽象化。
例子:
// exp1
class Animal {
final int MAMMAL = 0, BIRD = 1, REPTILE = 2;
int myKind; // set in constructor ...
String getSkin() {
switch (myKind) {
case MAMMAL: return "hair";
case BIRD: return "feathers";
case