代码重构方向原则指导

重构是一种对软件进行修改的行为,但它并不改变软件的功能特征,而是通过让软件程序更清晰,更简洁和更条理来改进软件的质量。代码重构之于软件,相当于结构修改之于散文。每次人们对如何对代码进行重构的讨论就像是讨论如果对一篇文学作品进行修订一样无休无止。所有人都知道应该根据项目的自身情况来对代码进行重构,而重构是无止境的。莫扎特从来不不对他的作品进行修订,特罗洛普对自己作品修订的恰到好处,大多数作家认为他们俩这样做都是合适的,但他们的合适对于你我来说未必是合适的。

最常见的基本重构方法可以归纳为两个方向。通过归纳方法将一个长的过程分解为小的可以重用的组件,和通过内联(inline)方法来消除那些不够份量的小方法。我们可以提炼方法来让大量的子类共享相同的功能特征,我们可以下放方法来让只有用到这些功能的子类才知道它们的存在。重构就是爬山,通过一步一步的小的提高来逐渐的改进整体的质量,但在重构时,我们如何知道哪种方法是上山的正确道路?

关于代码地形学的这个问题公认的方法有两种。去除有异味的代码重构成模式。如果能做到这样,当然是很好的。就像是纠正作文里的一个语法错误或不恰当的比喻。如果我们可以找到这些四处隐藏的有异味的代码,将它们重写成整洁的,条理的,结构化的形式,何乐而不为。但这些都是特殊情况。如果没有明显的模式来重构,或没有很直接的方法来去除代码异味,那该怎么办呢?

这才是我们如今编程艺术的中心问题,而很少人讨论这些。通常我们讨论这些问题时都是罗列出更多更长的有异味的代码模式的清单,但这并不是解决问题的方法。代码异味应该是我们公认的不好的东西,而不是那些置之不理也无妨的事情。我们经常会说到老板不给我们重构的机会,甚至代码有明显的异味,老板们认为这是浪费时间。并不是每个人都有懂软件的老板。我很吃惊为什么只有很少的讨论谈到点子上。。也许我这篇文章才说到问题关键处。

我的观点,当重构没有现成的明显的方向时,我们可以遵循下面的原则:

  1. 当属性、方法或类存在任何的需要复用的意向时,归纳提炼它们。
  2. 不要低估小方法对代码整洁的作用。使用小方法能让你节省很多笔墨。
  3. 能让代码长度变短的提炼都应该去提炼,包括注释。
  4. 用多形代替switch()——即使这样做会使代码变长。
  5. 用封装控制可见度。
  6. 消除依赖。
  7. 简化构造方法——即使这样做会使代码变复杂。
  8. 封装或避免条件表达式。使用guard语句,避免使用else语句。
  9. 使用常量代替魔幻数字。
  10. 不确定时,偏向使用组合而不是继承。
  11. 不确定时,将计算操作移入到这些数据的所有者对象里,或将数据移动到执行计算操作的对象里(也就是迪米特法则(Law of Demeter))。
  12. 使用小对象,松耦合,避免大对象,高聚合。
  13. 不确定时,偏向使用递归而不是循环。
  14. 使用代理对象,模拟对象和辅助对象来隔离网络,数据库,文件和用户接口。
  15. 不确定时,尽量在model里添加代码,必要时才往controler添加代码。view里添加的都应该是便捷功能和简写方法,但不要局限于此。
  16. 偏向使用apply, each, mapcar,而不是loop.
  17. 尽量使用新技术。
[英文原文:Hill Climbing (Wonkish) ]
分享这篇文章:

15 Responses to 代码重构方向原则指导

  1. QZWEWE says:

    4.用switch()代替多形——即使这样做会使代码变长。这条是不是应该是“宁可用多态面不用switch()“。

  2. fushan says:

    第八条:什么叫guard语句?
    第九条:什么叫魔幻数字

    • askfor says:

      比如
      if user_has_login:
      if user_has_permission:
      do_some_thing
      使用guard语句就是

      if not user_has_login:
      return
      if not user_has_permission:
      return

      do_some_thing

      magic number也有叫hard code

      • emugod says:

        魔幻数字就是程序中突然出现的一组未加注释的数字,当别人读到这段时根本不知道代表了什么意思,就像魔法师用的魔法数字,让人感觉莫名其妙.

        • Sioux says:

          guard语句是 代码大全里面提到的防御式编程。。 很久很久以前不晓得哪里出来一个一个函数只要有一个返回值的说法, 弄得 代码里面全是if else 的判断。

  3. 纪红玉 says:

    我有几点都不太同意你的观点。
    >能让代码长度变短的提炼都应该去提炼,包括注释。
    —-注释是为了能看懂,你丢给提炼了为了短,偏离了目的
    >用封装控制可见度。
    —我不知道我知道的开闭包,是不是解决方法,还是我混淆概念。
    >简化构造方法——即使这样做会使代码变复杂。
    —-请说明为什么,,不明白
    》不确定时,偏向使用递归而不是循环。
    —–你不知道栈溢出么?
    >尽量使用新技术。
    —你不知道稳定性么?你是都用scala写的?还是go写的

    • askfor says:

      >能让代码长度变短的提炼都应该去提炼,包括注释。
      我看了下原文,应该是翻译有点问题。including comments是指前面说的代码长度是包含comments的。
      也就是说如果这个extraction只会减少注释的长度,代码还是不变,也值得做。
      最常见的就是将注释变为新抽取出来的方法名,从而消除注释。

      Extract whenever extracting reduces the length of code — including comments

      • 纪红玉 says:

        那其他的问题呢?你还没说清楚,说明白呢。很多观点是值得推敲的。在线等您回复,我也想弄清心中的疑问

        • askfor says:

          我只能说说我能回答的。这些本身就是一个很主观的问题,有不同看法也很正常。另外作者也没有说必须怎样。

          >用封装控制可见度。
          —我不知道我知道的开闭包,是不是解决方法,还是我混淆概念。

          原文是visibility,强调的是内部实现没有暴露。你是否说的是作用域。

          》不确定时,偏向使用递归而不是循环。
          —–你不知道栈溢出么?

          作者说的很清楚了,不确定时,你知道有溢出的危险了那就用循环去。

          • 纪红玉 says:

            我只是对重构的这几个原则,有些疑义而已,另外感觉此文字站的角度过于偏向实现,站在全局的角度,站在架构的角度的原则指导,我到是没有怎么发现。在结合这个帖子的banner感觉有些落差~。好吧。我估计你也不喜欢和我讨论。那我还是吃中饭去吧。

          • askfor says:

            这些不是原则。
            只是可以考虑的方向。

  4. 美女优优 says:

    我的原则,先以目前熟悉的方式去实现功能,如果后面有更好的方式,从不会介意去重构,但这些都是在保证功能的正确性和完整性前提下的,同时不会影响项目时间

  5. Jim says:

    “莫扎特从来不不对他的作品进行修订” 有点疑问

  6. WindProtect says:

    以我现在会的方法把这些功能做出来,然后下一个项目又来了。根本没时间重构。我做外包的。那代码都是被逼着赶出来的,看都不想再看一次了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.