重写、重构的摘要和感想

缘起

最近在忙微盒主页,之前的主页开发是由@雨宮汐维护的。由于网页代码是别人写,不了解,在网页加入一些东西时显得困难。所以冒出了要不自己 重写一个吧的念头。

碰巧,在@IT技术博客大学习看到关于重构和重写的文章,接下来又看了你永远不应该做的事(这篇文章用 Netscape当例子)、什么是重构,什么不是重构网站重构到底是什么,网站重构到底要多久如何避免重构带来的危险一系列的文章。强烈建议去看完整的文章。下面将是一些摘要及感想:

什么是重构,什么不是重构

重构(Refactoring)这个词最初由Martin Fowler 和 Kent Beck给下的定义,它是:

一种修改,使软件的内部结构更容易理解,在不改变软件的可见行为方式前提下使软件更容易变更…它是一种有节制的 整理代码、使bug产生几率最小化的方法。

重构的结果是引用了快捷方法、去除了重复代码和死代码,使设计和逻辑更加清晰。 重构很简单。尽可能在写代码前先写测试能够防止你犯错误。小规模的、独立的、稳妥的对代码进行结构上的调整,每次调整完后都要进行测试,确保你没有改变代码的行为特征—— 功能和以前一样,只是代码上看着不同。

在这个过程中发现了bug、修改bug,这不是重构。优化不是重构。强化异常捕捉、增加预防性代码不是重构。让代码更容易测试不是重构—— 尽管重构能达到相同的效果。这些所有的事都是有益的。但这些都不是重构。

你不是为了重构而重构,你重构是因为你想做其它的事情,而重构能帮助你完成这些事情。不要对那些你不打算进行变更或不会变更的代码进行重构。

为理解而做简略重构(Scratch Refactoring)

Michael Feather的《Working Effectively with Legacy Code》这本书里提到了简略重构(Scratch Refactoring)的概念;Martin Fowler称之为 “为理解而重构”。这是用来对付那些你不理解的(或不能忍受的)代码,清理它们,这样在你打算真正动手修改它前,你能对它们是干什么的有了更好的理解,同样也对你debug这些代码有帮助。 一旦你能清楚了一个变量或方法的真正意图,重命名它们,给它们一个更合适的名称,删除那些你不喜欢看的(或觉得没有用的)代码,拆解复杂的条件语句,把长程序分解成数个容易理解的小程序

重构可以、也应该融入到你写代码或维护代码的过程中——作为日常开发/质量管理的组成部分,就像写测试和代码审查一样。 重构应该被安静的,持续的和低调的完成。它需要我们把工作精力分出一部分给它,它需要在我们的工期评估和风险评估中考虑到它的存在。 如果做的正确,你不需要去解释或向外人验证这部分工作。

花几分钟、一两个小时做重构,就像是你开发过程中的一种修改,是工作的一部分。 如果它让你花了数天时间,或者更长,那不是重构;那是重写,或重新设计。如果你需要明确的留出一部分时间(或整个sprint周期)来重构代码,如果需要为清理代码而申请批准,或把清理代码作为一个开发需求,那你不是在重构——即使你用了重构的技术和工具,你仍然做的是另外一种工作。

有些程序员认为对代码进行根本的、重大的修改是他们的权利和义务,在重构的名义下进行重新设计、重写,为了将来,也不辜负自己的技艺。重新设计和重写有时候是你正确的该做的事情。 但出于坦诚和表述清楚,请不要把这些活动赋以重构的名义

感想:

我以前都认为重构就是重头再来一遍,看来我误会了,幸好在我对微盒主页进行重写之前看到这些文章,不然就是浪费时间做无用功,而且还有可能阻碍到别人。好吧,我已经弄坏了,让原来的按钮都失效了。

关于重构和重写

程序员以及经理提到重写是基于以下几个理由:

  1. 对完全理解原有代码的恐惧
  2. 作为拖延项目完成时间的借口
  3. 希望表现自己的能力,觉得自己重新写比修复别人的工作更有挑战性与创造性
  4. 不了解重构

而上级能够采纳重写意见一般是基于:

  1. 原有项目时间拖延太长,修改难度越来越高,修复时间似乎是非线性增长的.
  2. 所有程序员都建议重新再来,没人愿意进行修复原有代码的工作

问题在于:

  • 当你不能够理解以及改善破败代码的时候,你凭什么能够认为能够写出一个功能一致并且更清晰的软件呢?
  • 首先你认为原来的代码已经是破败不堪,散发着腐败的臭味。
  • 你不能够靠读它去理解它,因为写的太烂了。但是你如果不读它.你又如何能理解它干了什么呢?
  • 凭借着从头开始进行复制文档,你又可能忽略多少隐藏在这破败代码沟壑中的细节呢?
  • 这些细节必然会重复出现,直到你重新遇见问题.找到它,修补它。
  • 当你没有能力修整破败代码的话,你修补这些细节之后的代码也会是破败的。
  • 当你无法理解别人破败代码,最终你也无法理解你自己写出来的破败代码。
  • 重构不是什么高深的技艺.但它需要磨练,但是我遇到很多人.都不会去修炼这项技能.
  • 迫于项目压力或者惰性,以及古老工匠守则的规劝: ”当一个事物运行正常的时候,不要碰它”
  • 和测试一样,至今程序员都没有把测试行为和代码结合在一起来做。
  • 手工作坊式的做法至今仍在,当然作坊式的手法有其优点。
  • 不过既然你已经是靠手艺吃饭,那么重构的手艺以及测试的手艺才是我们需要加强的, 而不是继续锻炼靠运气编程的手艺。

感想:

我会试着弄懂@雨宮汐写的微盒主页 的代码,然后改善一下,比如变量名,拆分冗长的结构还有添加一些注释,方便添加和删除一些元素和功能。

你永远不应该做的事——重写:

有一个微妙的原因使得程序员总是想抛弃原有的代码重新开始。原因是他们认为原来的代码太混乱。而且有一个很有趣的现象:他们可能错了 。他们认为旧代码混乱的原因是由于编程上的一个基本的,主要的定律:

阅读代码比写代码要困难

新代码要比旧代码好的观点是荒谬的。旧代码已经在使用了。已经被测试过了。大量的Bug已经被发现了,而且被修复了。这样并没有什么不好的。把程序放在硬盘中并不能使你发现BUG。难道软件像老Dogde Dart车一样,有生锈菌会在车库里等着?难道软件像玩具熊那样如果不用新材料就不会有那么多毛?

一个很重要的事情你要记住, 当你重写代码并没有绝对的理由相信你会比旧代码更好。首先,你甚至没有和写第一个版本一样的开发团队,因此你并不是"更有经验"。我会再一次犯大多数以前犯过的错误,而且会比原始的版本增加更多的新问题。

感想:

应该去看原文的Netscape例子。觉得重写代码会把之前的别人的经验教训都扔掉了,而且还是重复劳动比较多。况且我现在还没有能力做出比别人更好的轮子。

如何避免重构带来的危险

重构代码很危险,它会给测试工作增加巨大的负担。除非你的程序需要重构,一定不要轻易重构代码。我这里所说的并不是把一个for循环改成while循环,或把一个StringBuffer改成StringBuilder,我说的是大动作,例如重写一个方法,一个函数,甚至整个类或包。如果你缺乏对一个方法或一个类的了解,那你重构它的条件就不充分。即使你有一个天才的计划,你也需要和团队一起设计其中重大的修改。

当属于下列情况时,你不该重构:

  • 对于你来说,它的逻辑看起来过于复杂,你没有花时间去分析它。

  • 你不理解为什么前任程序员要这样编写。

  • 你着手的是一个很重要的系统,而且时间很紧。

  • 你是团队里的新成员,或新接触这个项目,或这种语言。

  • 当属于下列情况时,你可以重构

  • 现有的代码对它要实现的功能显得过于复杂,并且你分析过它。

  • 修改后的代码远比现存的代码逻辑要清晰。

  • 你有足够的时间,人手,财力来支持对项目进行回归测试。

  • 现有的代码陈旧无效率。

  • 无人认领的,写的很烂的代码都属于此类。

  • 跟你的一位同事谈论对这部分程序进行重构的好处和存在的风险,你们两个都赞成重构。

如何降低重构的风险:

  • 权衡一下对一段代码进行重构的利与弊,找出降低风险的方法。调试一段你经过重构但却使产品崩溃的代码,这对你来说将会是在这个行业中最有压力的事情。

  • 使用自动化的回归测试,快速的验证你的修改。这非常重要,如果没有准备自动化测试,你应该在做任何修改前建好它。

  • 尽量让你的重构处于很短的开发周期,产品更新发布周期也尽可能短。

  • 把你重构的代码和其它程序隔离开,这样能让你更容易找到出问题的地方。

  • 为你的重构活动准备测试计划,包括回归测试,功能测试,反向测试,负载测试,性能测试和用户确认测试。

  • 投入全部精力来研究其中的逻辑,不要分心做其它事情。

  • 在需要的地方使用设计模式。不要为了设计模式而增加设计模式。设计模式应该用在合适的时间和合适地方。

  • 小粒度重构

当你在开封一个方法时,如果你发现其中有一部分可以改进,那你就该考虑它,改进它。整洁的代码是我们需要的,因为写的很烂的代码我们到处可见。和你的同事讨论它们,当有人要修改你的代码时不要固守己见。重构,然后回归测试,然后才提交代码。没有人希望自己提交的代码会弄垮系统。

下面是一些比较有深度的阅读材料。

忍住你的欲望,不要试图重构你不理解的代码。多问问题,努力能清楚他们为什么要把程序写成这样。也许他们有很好的理由。如果你找到一段很古老的代码,很有可能它们是按照古老的方式写的。每天都在新增的API,模式,需求和新领会都会让这些老的方式显得陈旧。 不断努力学习新的技术,但不要为了要使用这些技术而过于热心的在重构中使用它们。 (PS: 当你拿起锤子,就觉得哪里都是钉子这句话就是表达了对新学技术的滥用,谨记)

感想:

忍住你的欲望,不要试图重构你不理解的代码。好吧,之前我就在我不理解的情况下,把原来的一些功能改得全部失效了。尽管后来恢复了效果,但是不敢再贸然下手,还是先理解了再说,多交流。

后记

我是初学者,半吊子不靠谱。十分感谢微盒主页这个项目,让我有练手的机会,还请各位多多包涵。刚开始加入这个项目的时候,我打算自己增加一些元素(我审美观有点二啊,( ̄ε(# ̄))。由于不理解,贸然下手,花了好长的时间才加入一个动态LOGO。然后就像文中所说的,想重新写一遍(好吧,我果然是初学者,又犯二了( ̄ε(# ̄) )。然后就看到这几篇文章,深有感触。打算好好学下HTML/CSS的基础,然后重构下wecase的项目主页。就是不改变外在表现和功能前提下,弄清楚了一个方法的真正意图,重命名,给它们一个更合适的名称,把长段代码分解成数个容易理解的代码片段,添加注释,让其结构化,容易扩展,方便增删功能。

牢记理念,继续实践