《Refactoring Improving the Design of Existing Code》笔记

06 December 2016

读后感 重构

任何一个傻瓜都能写出计算机可以理解的语言,唯有写出人类容易理解的代码,才是优秀的程序员。

设计模式为重构提供目标。然而“确认目标”只是问题的一部分而已,改造程序以达到目标,是另一个难题。

学会如何以一种可控制且高效率的方式进行重构。嗅出代码中的坏味道。

目录

  • 1.什么是重构?
  • 2.为什么要重构?
  • 3.什么时候重构?
  • 4.何时不该重构?
  • 5.重构与设计
  • 6.重构与性能
  • 7.代码坏味道
  • 8.重构方法
  • 9.复用与现实
  • 10.总结

1.什么是重构?

在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。提高可读性,降低修改成本。

一句话:重构就是在代码写好之后改进它的设计。

1.1 重构的问题

重构具有风险。它必须修改运作中的程序,这可能引入一些不易察觉的风险。

重构方式不当,会毁掉你数天至数星期的成果。

重构时不做准备,不遵守规则,风险更大。

重构必须系统的进行。

2.为什么要重构?

2.1 重构改进软件的设计

没有重构,程序的设计会逐渐腐败变质。贸然修改代码,程序将会失去自己的结构,程序员会越来越难通过阅读源码来理解原来的设计。

比喻:重构很像整理代码,你所要做的事情就是让所有的东西回到原来的位置上。

代码结构的流失是积累性的。

越难看出代码的设计意图,就越难保护其中的设计,于是该设计就腐败的越快。

总结:经常性的重构可以帮助代码维持自己该有的形态。

2.1.1 消除重复代码

动机:方便未来修改

原因:代码量减少,修改程序就会容易。代码越多修改代码就很困难,因为代码阅读就需要更多时间去理解。

确定所有事物和行为在代码中只表达一次,这就是优秀设计的根本。

2.2 重构使软件更容易理解

编程模式的核心就是“准确说出我想要的”。

原因:当你努力让程序运转的时候,不会想到未来出现的那个来修改你代码的开发者。

改变一下开发节奏,对代码做适当的修改,让代码变得更易理解。重构可以帮助我们让代码更易理解。

让代码更好的表达自己的意图。

动机:利用重构来协助我理解不熟悉的代码。

领悟:随着代码的整洁,我发现自己可以看到一些以前看不到的设计层面的东西。

2.3 重构帮助找到 bug

对代码进行重构,我就可以深入的去理解代码的行为,并恰到好处地把新的理解反馈进去。搞清楚程序结构的同时,我也清楚了自己所做的一些假设,于是不把bug揪出来都难。

重构能够帮助我更有效的写出强健的代码。

2.4 重构提高编程速度

终点:重构帮助你更快的开发程序。

绝对相信:良好的设计是快速开发的根本—事实上拥有良好设计才可能做到快速开发。

你必须坚信:“恶性循环”。

原因:重构阻止了系统腐败变质,它甚至可以提高编程质量。

3.什么时候重构?

??? – 怎样安排重构时间表。我们是不是应该每两个月就专门安排两个星期来进行重构?

作者观点:重构本来就不是一件应该特别拨出时间做的事情,重构应该随时随地进行。你不应该为重构而重构,你之所以重构,是因为你想做别的什么事,而重构可以帮助你把那些事做好。

3.1 三次法则

“事不过三,三则重构”。 — 第一次做某事时只管去做;第二次做类似的事会产生反感,但无论如何还是可以去做;第三次再做类似的事,你就应该重构。

3.2 添加功能的时候重构

原因:如果在前进过程中把代码结构理清楚,我就可以从中理解更多东西。

重构动力:代码的设计无法帮助我轻松添加我需要的特性。

一旦完成重构,新特性的添加就会更快速,更流畅。

3.3 修补错误时重构

因为显然代码还不够清晰–没有清晰到让你一眼看出bug.

3.4 复审代码时重构

有助于让有经验的开发者把知识传递给比较欠缺经验的人,并帮助更多人了解系统的更多部分。

3.5 _.compose(函数式编程)

重要的是做什么,而不是怎么做

3.6 _.curry(函数式编程)

函数柯理化(curry) : 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

4.何时不该重构?

  1. 应该重新编写所有代码的时候

  2. 重写的“讯号”:现有的代码根本不能正常运作,重构之前,代码必须起码能够在大部分情况下正常运作(运作不是运行)

折中方法:将“大块头软件”重构为封装良好的小型组件,然后可以逐一对组件做出“重构或重建”的决定。对遗留系统这是一个很好的方向。

3.项目最后期限,应该避免重构。

5.重构与设计

设计与重构互补。

问题??? – 是预先设计还是先编码?

关键在于:重构改变了预先设计的角色。如果没有重构,你就必须保证预先做出的设计是正确无误,这压力太大了。这意味着如果将来需要对原始设计做任何修改,代价都将非常昂贵。因此你需要把更多的精力放在预先设计上,以避免日后的修改。如果选择重构,问题的重点就转变了。你任然做预先设计,但是不必一定要找出证正确的解决方案,此刻的你只需要得到一个合理的解决方案就够了。

把一个简单的解决方法重构成一个灵活的解决方法有多难?答案是:“相当容易”。

6.重构与性能

编写快速软件的秘密:首先写出可调的软件,然后调整它以求获得足够速度。

三种编写快速软件的方法

  • 时间预算法

    用于性能要求极高的实时系统。

  • 持续关注法

    要求任何程序员在任何时间做任何事时,都要设法保持系统的高性能。

  • 性能提升法

    按照某个特定程序来调整性能。

7.代码坏味道

  1. Duplicated Code (重复代码)

  2. Long Method (过长函数)

  3. Long Class (过大的类)

  4. Long Parameter List (过长参数列)

  5. Divergent Change (发散式变化) 一个类受多种变化影响。

  6. Shotgun Surgery (霰弹式修改) 一个变化引发多个类相应修改。

  7. Feature Envy (依恋情怀)

  8. Data Clumps (数据泥团)

  9. Primitive Obsession (基础类型偏执)

  10. Switch Statements (switch 惊秫现身)

  11. Parallel Inheritance Hierarchies (平行继承体系)

  12. Lazy Class (冗赘类)

  13. Speculative Generality (夸夸其谈未来性)

  14. Temporary Field (令人迷惑的暂时字段)

  15. Message Chains (过度耦合的消息链)

  16. Middle Man (中间人)

  17. Inappropriate Intimacy (狎昵关系)

  18. Alternative Classes with Different Interfaces (异曲同工的类)

  19. Incomplete Library Class (不完美的类库)

  20. Data Class (纯稚的数据类)

  21. Refused Bequest (被拒绝的遗赠)

  22. Comments (过多的注释)

8.重构的方法

重构方法

重构方法

9.复用与现实

9.1为什么还不肯重构你的程序?

  • 1.你不知道如何重构?
  • 2.如果这些利益是长远的,何必现在付出这些努力呢?长远来看,说不定当项目收获利益时,你已经不在这个职位上了。
  • 3.代码重构是一项额外的工作,老板付钱给你,主要让你编写新功能。
  • 4.重构可能破坏现有程序。

9.2如何进行安全重构?

  • 相信自己的编码功力。
  • 相信你的编译器能捕捉到你遗漏的错误。
  • 相信你的测试套件能捕获到你和编译器遗漏的错误。
  • 相信代码复审能捕捉到你,编译器和测试套件遗漏的错误。

10.总结

“我不是个伟大的程序员,我只是个有着一些优秀习惯的好程序员”。 —Kent Beck

参考文档

Refactoring Improving the Design of Existing Code

改善既有代码的设计