owenzhang的博客

谈一谈代码重构

字数统计: 1.6k阅读时长: 5 min
2013/10/27
loading

上周进行小组分享的时候讲的代码重构,从《代码大全》《clean code》《重构》三本书中摘抄了一些观点,结合日常开发的例子讲解了以下。发现重构不像工具学习,听了一节课马上就有效果,能解决一个问题,更多的还是要培养一种意识。在开发代码的时候不引入错误不健康的习惯,在维护代码的时候能够收住有味道的代码,不让项目变得更糟。

当然,做这种事不会有直接利益,不像查找个内存泄漏更容易让人佩服。但是好的编码习惯是自己终身的财富,就像一个彬彬有礼的人,平时可能会“吃亏”,但是最终会受益的。

##不要抱怨

这段代码真烂!

通常新维护别人的代码时都会发出感慨,然后加500字的嘲笑,或者和其他人讲述那个模块如何地令人恶心……
可是有用吗,代码还是那样,依旧在那里,该脏还是脏。

更好的方式应该是想“如何优化这段代码”,或者和另外一个小伙伴商量医治这段代码的手段。解决问题才是真,不要抱怨。
##什么是重构
重构不是重写!

重构还是要保持代码最开始的框架和功能,对外来说,黑盒的接口没有变化。对内来说,框架还是最初的框架。目的还是让代码更容易维护。

不要把重构当作重写的接口,不要在刚写软件的时候就想以后会重构。修改软件的最好时机就是在他被初创的时候,一旦对外发布,在想改变,就要测试、评估,代价越往后越高。而且“do it later = do it never”
##重构的优先级
从简单到复杂。先修改简单的,对主逻辑影响小或干脆没影响的,测试压力会小一些;然后修改逻辑重的部分,最后再修改核心算法。一次只修改一个地方,方便观察和回溯。
##重构的理由

  • 代码重复
  • 代码冗长
  • 参数太多
  • 使用全局变量
  • 过渡设计
  • ……

见过最多的就是代码重复,冗长。一个文件上万行,代码逻辑比较简单,增加逻辑就添加个if分支,表面上是省事了,但是给扩展性增加了困难。

总之就是代码有味道。发现可以精简,做得更好的地方,在修复bug,增加子程序时顺手给处理掉。“让营地比来的时候更干净”。
##具体的重构方法

  • 用具名常量代替魔数。
  • 将一组类型码转换为类或枚举

经常见到的就是返回值,都是用return 1,2。第一个人写的时候知道意思,后面的人写的时候就不知道了,然后自己往上填,造成返回值冲突,判断错误。最好的是有一个枚举类型的返回值,返回的时候开发也不能随意填写,要按照枚举的类型进行返回。

  • 使用break和return而不是循环控制变量
  • 将条件语句中不同部分中的重复代码合并
  • 在嵌套的if语句中一旦知道结果就马上退出

可以防止后面增加其他的分支或修改时,无意间破坏原有的返回值逻辑,造成返回值被意外篡改,而且对于阅读代码的人,可以很清晰的知道到这就结束了,而不用再跳来跳去,检查是否还有别的东西。

  • 去掉不用的参数
  • 增加参数,而不是一个struct把参数都包括了
  • 合并功能相似的子程序,用参数区分

曾经也很纠结,到底如何处理类似的程序,用参数还是不同的函数名。用参数更有扩展性,而且新增功能时可以方便的进行测试。用老的测试例子就可以完成。

某些参数不用了,最好去掉,否则会干扰调用。一个不用的东西摆在那里,总会有人去想看看到底有没有用,浪费时间。

经常遇到的情况是在一个程序里有个CONFIG类参数,里面包括了要用的大部分参数。后面想把这个函数用到另外一个程序里,发现很难移植,因为要编写一个相同的类,类的成员名字也要相同,真是类死个人。最好的方法是尽量用基本的数据类型,如果类需要调用,那么把类成员展开调用就可以了。

我觉得,让代码好维护,就是尽量复用,不要重复。让各个逻辑模块间逻辑清晰,减少耦合。维护的时候才能只关心修改部分,不会节外生枝。

##安全地重构
留好后路,有svn、git等版本控制工具。步伐尽量小,有检测代码重构后是否逻辑有影响的监控机制。更多地测试和监控。

##总结

有些人觉得做了这些也没人看得到,而且还很麻烦,很多人也都不这么做。有句话“佛会知道”,多行善会有善报的,出来混迟早是要还的。再说了,大家都不这么做,而你这么做,不是很有优势吗?!

##补充
2013-11-11
上个星期主要工作是在重写一个模块,虽然我们都叫重构。由于从前都是采用同步方式进行写操作,效率不高。这次打算都作成异步的。
开始以为会很简单,但是工期大大超期。原因如下:

  1. 从前逻辑太过于复杂,函数封装不好,重用性差。
  2. 边写代码边了解逻辑,没有文档。
  3. 迁移都协议都比较重要,但是并不是性能瓶颈,访问量不大,但迁移的风险比较大。

如果时间允许的情况下,我觉得应该这样做。

  1. 先在老代码上修改,封装集成函数。让开发充分了解代码逻辑,把函数抽象。
  2. 持续发布,利用老框架测试新代码。
  3. 如果没有达到瓶颈就不迁移到异步命令,保持代码整洁封装性强即可。
CATALOG