重构的原则

  1. 何为重构:

    • 很多人使用 「重构」 这个词来指代任何形式的代码清理。
    • 重构的关键在于运用大量微小且保持软件行为的步骤,一步步稻城大规模的修改。每个单独的重构要么很小,要么由若干小步骤组合而成;因此,在重构的过程中,代码很少进入不可工作的状态,即便是没有完成重构,也可以在任何地方停下来;
    • 「可观察行为」:整体而言,经过重构的代码所做的事应该与重构之前大致一样;
  2. 两顶帽子:

    • 把时间分配给两种截然不同的行为:添加新功能和重构;
    • 添加新功能时,不应该修改既有代码,只管添加新功能,通过测试让程序正常运行;
    • 重构时,不能再添加新功能,只管调整代码的结构,此时不应添加任何测试。只在绝对必要时才去修改测试;
    • 软件开发工程中,可能会发现自己会经常换帽子,但无论何时都得清楚自己戴的事那一顶帽子,并且明白不同的帽子对变成状态提出的不同要求;
  3. 为何重构:

    • 重构改进软件设计:

      • 如果没有重构,程序的内部设计会逐渐腐败变质;
      • 代码结构的流失有累积效应,越难看出代码所代表的设计意图,就越难保护其设计,于是设计就腐败的越快。
      • 经常性的重构有助于代码维持自己该有的形态;
      • 改进设计的一个重要方向就是消除重复代码,代码量减少并不会使系统运行更快,因为这对程序的资源占用几乎没有任何明显影响,然而代码量减少将使未来可能的程序修改工作容易的多;
      • 消除重复代码,就可以确定所有事物和行为在代码中只表述一次,这正是优秀设计的根本;
    • 重构使软件更容易理解:

      • 重构可以帮我让代码更易读;
    • 重构帮助找到 bug :

      • 对代码的理解,可以帮我找到bug;
      • 对代码进行重构,就可以深入理解代码的所作所为,并立即把新的理解反映在代码当中;
      • 重构可以帮助开发者更有效的写出健壮的代码;
    • 重构提高编程速度:

      • 重构可以帮助开发者更快速的开发程序;
      • 重构可以改善设计,提升可读性,减少bug,这些都能提高质量。花在重构上的时间确实可以提高开发效率;
      • 如果代码清晰,引入 bug 的可能性就会变小,即使引入了 bug ,调试也会容易的多。理想情况下,我的代码库会逐步演化成一个平台,在其上可以很容易的构造与其领域相关的新更能;
      • 通过投入精力改善内部设计,增加了软件的耐久性,从而可以更长时间的保持开发的效率;
      • 由于预先作出良好的设计非常困难,想要既体面又快速的开发功能,重构必不可少;
    • 何时重构:

      • 三次法则:当代码重复出现三次,就必须要重构了;

      • 预备性重构:

        • 重构的最佳时机就是在添加新功能之前,在动手添加之前,先要根据原有代码库设计好重构方法;
        • 「出发前先规划好最有的路线很重要」;
        • 修复 bug 时的情况也是一样,如果把3段一摸一样且都会导致错误的代码合并到一处,问题修复起来会容易的多。或者,用重构改善这些情况,在同样场合再次出现同样的bug 的概率和会降低;
      • 帮助理解的重构,使代码更易懂:

        • 需要首先理解代码再做什么,然后才能着手修改;
        • 通过重构,就把业务的理解转移到代码本身,随后运行软件,检查是否正常运行,来检查这些理解是否正确;
        • 如果把对代码的理解植入代码中,这份知识会保存的更久,并且我的同事也能看到;
        • 当代码变得清晰时,就会看到之前看不见的设计问题;
        • 在研读代码时,重构会引领开发者获得更高层面的理解,如果只是阅读代码很难有此领悟;
      • 捡垃圾式重构:

        • 遇到问题代码时,进行少量的修复不影响正常逻辑;
        • 如果每次经过这段垃圾代码时都把它变好一点,积少成多,垃圾总会被清理干净。重构的妙处在于,每个小步骤都不会破坏代码,所以,有时一块垃圾代码在好几个月之后才终于清理干净,但即使每次清理并不完整,代码也不会被破坏;
      • 有计划的重构和见机行事的重构:

        • 预备性重构,帮助理解性重构,捡垃圾式重构,都是见机行事的,并不会安排一段时间来重构,而是在添加功能或者修复 bug 的同时顺便重构,这是自然编程流的一部分;
        • 绝大多数重构都是开发者在做其他事的过程中自然发生;
        • 「每次要修改时,首先令修改很容易,然后进行这个容易的修改」
        • 优秀的程序员知道,添加新功能最快的方法往往是先修改现有的代码,是新功能容易被加入。所以,软件永远不应该被视为“完成”,每当需要新能力时,软件就应该作出相应的改变,越是在已有代码中,这样的改变就越显重要;
        • 重构的思想要应用在日常开发中,不应该在专门的时间内;
        • 重构常与新添加功能紧密交织,不值得花工夫把他们分开,并且这样做也使重构脱离了上下文,使人看不出这些“重构提交”的价值;
      • 长期重构

        • 如果不愿让一支团队专门做重构,可以让整个团队达成共识,在未来几周时间里逐步解决这个问题,这是一个有效的策略,好处在于不会破坏代码,每次小改动之后,整个系统仍然照常工作;
      • 复审代码时重构

        • 重构可以帮助开发者复审其他同事的代码,开始重构前我可以先阅读代码,得到一定程度的理解,并提出一些建议,一旦想到一些点子,我就会考虑是否可以通过重构立即实现它们;
        • 重构可以帮助代码复审工作得到更具体的结果;
        • 结队编程:在编程的过程中持续不断的进行代码复审;
      • 怎么对经理说重构

        • 不要跟经理说代码重构!!!
        • 如果要修补错误,就得先理解软件工作方式,而我发现重构时理解软件的最快方式;
      • 何时不应该重构

        • 只有当我需要理解其工作原理时,对其进行重构才有价值;
        • 如果重写比重构还容易,就别重构了;
      • 重构的挑战

        • 了解重构会遇到的挑战,这样才能做出有效应对;

        • 延缓新功能发布

          • 重构唯一的目的就是让我们开发更快,用更少的工作量创造更大的价值;
          • 绝大部分人应该尝试多做重构;
          • 我们之所以重构,因为它可以让我们更快,添加功能更快,修复 bug 更快;
          • 重构应该总是由经济利益驱动的;
        • 代码所有权

          • 修复一个函数名时,注意观察所有的调用者,是否有权限修改全部的调用者;
          • 代码所有权的边界会妨碍重构,因为一旦自作主张的修改,就一定会破坏使用者的程序;
        • 分支

          • 「语义冲突」 :在一个分支中修改了函数名字,但是如果在其他分支中大量使用修改前的函数名,则会引入大量编译错误,这个称谓语义冲突;
          • 团队中每个开发者都会在各自的分支上工作,做完然后合并到主分支上,「master」 称为:特性分支;
          • 特性分支的缺点:在隔离分支上工作的越久,将完成的工作合并会主分支上就越困难;
          • 如果我修改了一个函数名,版本控制工具可以轻松的将我的修改与别人的代码集成。如果在集成之前别人新添加调用了这个被我改名的函数,集成之后的代码就会被破坏;
          • 在使用 CI 集成工具时,每个团队成员每天至少向主线集成一次,这个实践避免了任何分支彼此差异太大,从而极大的降低了合并的难度;
          • 重构经常需要对代码库中的很多地方做很小的修改,这样的修改尤其容易造成合并时的语义冲突;
        • 测试

          • 不会改变程序可观察的行为,这是重构的一个重要特性;
          • 既然每个重构都是很小的改动,即便真的造成了破坏,只需要检查最后一步的小修改。就算找不到原因也只需要回滚到最后一个可用版本就行了;
          • 自测试代码与集成紧密相关,我们仰赖持续集成来捕获分支集成造成的语义冲突;
        • 遗留代码

          • 重构可以很好的帮助我们理解遗留系统,引人误解的函数名可以改名,使其更好的反映代码用途;
          • 开发者需要随时重构相关的代码;每次触碰一块代码尝试着把它变好一点,至少要让营地比我到达时更干净;
        • 数据库

          • 数据库是重构经常出问题的一个领域;
          • 和通常的重构一样,数据库的重构的关键也是小步修改并且每次修改都应该完整,这样每次迁移之后系统仍然能运行。由于每次迁移设计的修改都很小,写起来容易;将多个迁移串联起来,就能对数据库结构及其中存储的数据做很大的调整;
          • 数据库重构最好的是分散到多次生产发布来完成,这样即便某次修改再生产数据库上造成问题,也比较容易回滚;
        • 重构,架构和YAGNI

          • 「YAGNI」 : “你不会需要它”(you aren?t going to need it)的缩写。YAGNI并不是“不做架构性思考”的意思,不过确实有人以这种欠考虑的方式做事。我把YAGNI视为将架构、设计与开发过程融合的一种工作方式,这种工作方式必须有重构作为基础才可靠。
          • 重构极大的改变了人们考虑软件架构的方式;
          • 修改遗留代码经常很油挑战,尤其是遗留代码缺乏恰当的测试时;
          • 重构对架构最大的影响在于,通过重构,我们能得到一个设计良好的代码库,使其能够优雅的应对不断变化的需求;
          • 如果一种灵活性机制不会增加复杂度「比如添加几个命名良好的小函数」可以引入它;但如果一种灵活性会增加软件复杂度,就必须证明自己值得被引入。如果不同的调用者不会传入不同的参数值,那么就不会添加这个参数;
        • 重构与软件开发过程

          • 重构是否有效,与团队采用的其他软件开发实践紧密相关;
          • 重构起初是作为极限编程的一部分被人们采用的,极限编程本身就融合了一组不太常见而又彼此关联的时间,例如持续集成,自测试代码以及重构;
          • 重构的第一块基石是自测试代码;
          • 自测试代码也是持续集成的关键环节,所有这三大实践 ---- 自测试代码,持续集成,重构,彼此之间有着很强的协同效应;
          • 有这三大核心实践打下的基础,才谈得上运用敏捷思想的其他部分。
          • 持续交付确保软件始终处于可发布的状态,很多互联网团队能做到一天多次发布,靠的就是持续交付的威力;
          • 持续集成也能帮我们降低风险,并使我们做到根据业务需要随时安排发布,而不受技术的局限;
        • 重构与性能

          • 为了让阮家易于理解,我常会做出一些使程序运行变慢的修改;
          • 虽然重构可能使软件运行更慢,但它也使软件的性能优化更容易,除了对性能有严格要求的实时系统,其他任何情况下「编写快速软件」的秘诀就是:先写出可调优的软件,然后调优它以求获得足够的速度;
          • 关于性能:对大多数程序进行分析就会发现大半时间都耗费在一小半代码上,如果一视同仁的优化所有代码,90%的代码优化都是白费的,因为被优化的代码很少执行;
          • 如果因为缺乏对程序的清楚认识而花费时间,那些时间就都被浪费掉了;
          • 一个良好的程序可以从两方面帮助这一优化:首先,它让我有比较充裕的时间进行性能调整,因为有构造良好的代码在手,我能够更快速的添加功能,也就有更多的时间再性能问题上。「准切的度量则保证我把这些时间投在恰当地点」;
          • 其次面对构造良好的程序,在进行性能分析时便便有较细的粒度。度量工具会把我带入范围较小的代码中,而性能的调整也比较容易些。由于代码更加清晰,因此可以更好的理解自己的选择,更清楚那种调整起关键作用。
          • 重构可以帮助开发者写出更快的软件,短期来看,重构的确可能使软件变慢,但它使优化阶段的软件性能调优更容易,最终还是会得到好的效果;
本作品采用《CC 协议》,转载必须注明作者和本文链接
周小帅
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!