膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 .
代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提;
.
1. 重复代码 (Duplicated Code)
用到的重构方法简介 : Extract Method(提炼函数), Pull Up Method(函数上移), From Template Method(塑造模板函数), Substitute Algorithm(替换算法), Extract Class(提炼类);
-- Extract Method(提炼函数) : 将重复的代码放到一个函数中, 并让函数名称清晰的解释函数的用途;
-- Pull Up Method(函数上移) : 将函数从子类移动到父类中;
-- From Template Method(塑造模板函数) : 不同子类中某些函数执行相似操作, 细节上不同, 可以将这些操作放入独立函数中, 这些函数名相同, 将函数上移父类中.
-- Substitute Algorithm(替换算法) : 将函数的本体替换成另外一个算法;
-- Extract Class(提炼类) : 建立一个新类, 将相关的函数 和 字段 从旧类搬移到新类;
重复代码坏处 : 重复的代码结构使程序变得冗长, 这个肯定要优化, 不解释;
同类函数重复代码 : 同一个类中 两个函数 使用了相同的表达式;
-- 解决方案 : 使用 Extract Method(提炼函数) 方法提炼出重复的代码, 两个函数同时调用这个方法, 代替使用相同的表达式;
兄弟子类重复代码 : 一个父类有两个子类, 这两个子类中存在相同的表达式;
-- 代码相同解决方案 : 对两个子类 使用 Extract Method(提炼函数)方法, 然后将提炼出来的代码 使用 Pull Up Method(函数上移)方法, 将这段代码定义到父类中去;
-- 代码相似解决方案 : 使用 Extract Method(提炼函数)方法 将相似的部分 与 差异部分 分割开来, 将相似的部分单独放在一个函数中;
-- 进一步操作 : 进行完上面的操作之后, 可以运用 From Template Method(塑造模板函数) 获得一个 Template Method 设计模式, 使用模板函数将相似的部分设置到模板中, 不同的部分用于模板的参数等变量;
-- 算法切换 : 如果模板中函数的算法有差异, 可以选择比较清晰的一个, 使用Substitute Algorithm(替换算法) 将不清晰的算法替换掉;
不相干类出现重复代码 : 使用Extract Class(提炼类) 方法, 将重复的代码提炼到一个重复类中去, 然后在两个类中 使用这个提炼后的新类;
-- 提炼类存在方式 : 将提炼后的代码放到两个类中的一个, 另一个调用这个类, 如果放到第三个类, 两个类需要同时引用这个类;
2. 过长函数(Long Method)
用到的重构方法 : Extract Method(提炼函数), Replace Temp with Query(以查询取代临时变量), Introduce Parameter Object(引入参数对象), Preserve Whole Object(保持对象完整), Decompose Conditional(分解条件表达式);
-- Extract Method(提炼函数) : 将代码放到一个新函数中, 函数名清晰的说明函数的作用;
-- Replace Temp with Query(以查询取代临时变量) : 程序中将表达式结果放到临时变量中, 可以将这个表达式提炼到一个独立函数中, 调用这个新函数 去替换 这个临时变量表达式, 这个新函数就可以被其它函数调用;
-- Introduce Parameter Object(引入参数对象) : 将参数封装到一个对象中, 以一个对象取代这些参数;
-- Preserve Whole Object(保持对象完整) : 从某个对象中取出若干值, 将其作为某次函数调用时的参数, 由原来的传递参数 改为 传递整个对象, 类似于 Hibernate;
-- Replace Method with Method Object(以函数对象取代函数) : 大型函数中有很多 参数 和 临时变量, 将函数放到一个单独对象中, 局部变量 和 参数 就变成了对象内的字段, 然后可以在 同一个对象中 将这个 大型函数 分解为许多 小函数;
-- Decompose Conditional(分解条件表达式) : 将 if then else while 等语句的条件表达式提炼出来, 放到独立的函数中去;
小函数优点 : 小函数具有更强的 解释能力, 共享能力, 选择能力, 小函数维护性比较好, 拥有小函数的类活的比较长;
-- 长函数缺点 : 程序越长越难理解;
-- 函数开销 : 早期编程语言中子程序需要额外的开销, 所以都不愿意定义小函数. 现在面向对象语言中, 函数的开销基本没有;
-- 函数名称 : 小函数多, 看代码的时候经常转换上下文查看, 这里我们就需要为函数起一个容易懂的好名称, 一看函数名就能明白函数的作用, 不同在跳转过去理解函数的含义;
分解函数结果 : 尽可能分解函, 即使函数中只有一行代码, 哪怕函数调用比函数还要长, 只要函数名能解释代码用途就可以;
-- 分解时机 : 当我们需要添加注释的时候, 就应该将要注释的代码写入到一个独立的函数中, 并以代码的用途命名;
-- 关键 : 函数长度不是关键, 关键在于 函数 是 做什么, 和 如何做;
常用分解方法 : Extract Method(提炼函数) 适用于 99% 的过长函数情况, 只要将函数中冗长的部分提取出来, 放到另外一个函数中即可;
参数过多情况 : 如果函数内有大量的 参数 和 临时变量, 就会对函数提炼形成阻碍, 这时候使用 Extract Method(提炼函数) 方法就会将许多参数 和 临时变量当做参数传入到 提炼出来的函数中;
-- 消除临时变量 : 使用 Replace Temp with Query(以查询取代临时变量) 方法消除临时元素;
-- 消除过长参数 : 使用 Introduce Parameter Object(引入参数对象) 和 Preserve Whole Object(保持对象完整) 方法 可以将过长的参数列变得简洁一些;
-- 杀手锏 : 如果使用了上面 消除临时变量和过长参数的方法之后, 还存在很多 参数 和 临时变量, 此时就可以使用 Replace Method with Method Object(以函数对象取代函数方法) ;
提炼代码技巧 :
-- 寻找注释 : 注释能很好的指出 代码用途 和 实现手法 之间的语义距离, 代码前面有注释, 就说明这段代码可以替换成一个函数, 在注释的基础上为函数命名, 即使注释下面只有一行代码, 也要将其提炼到函数中;
-- 条件表达式 : 当 if else 语句, 或者 while 语句的条件表达式过长的时候, 可以使用Decompose Conditional(分解条件表达式) 方法, 处理条件表达式;
-- 循环代码提炼 : 当遇到循环的时候, 应该将循环的代码提炼到一个函数中去;
3. 过大的类 (Large Class)
用到的重构方法 : Extract Class(提炼类), Extract Subclass(提炼子类), Extract Interface(提炼接口), Duplicate Observed Data(复制被监视的数据);
-- Extract Class(提炼类) : 一个类中做了两个类做的事, 建立一个新类, 将相关的字段和函数从旧类中搬移到新类;
-- Extract Subclass(提炼子