说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

深度解析:清理烂代码

HTML文档下载 WORD文档下载 PDF文档下载
烂代码不一定是问题,只要它们没有出错,没有人会对它嗤之以鼻。但不幸的是,它们没被发现的概率太小了。错误会被发现。需要新的功能,新系统发布了。现在你不得不面对这堆恐怖的代码,试着去清理它们。

猜猜看怎么了!你正”继承“(接收)了一堆混乱的旧代码。恭喜你!现在都是你的了。混乱的代码可能来自任何地方。中间件,网络,可能来自你自己的公司。

你知道在一个角落里有一个家伙,没有人过去管他在做什么。猜猜看他一直在做什么?辛辛苦苦写出了代码,却是一堆烂代码。

你还记得这个模块是一个家伙几年前写的,在他离开公司之前。这个模块已经有20个不同的人加过补丁,进行过代码修复,而且他们也并不理解代码到底是做了什么。是的,就是这样的代码。

或者你从网上下载下的开源的软件,你知道它非常的可怕,但是它解决了一个非常专的并且对你来说非常棘手的问题,解决这个问题你可能要花上几年。

烂代码不一定是问题,只要它们没有出错,没有人会对它嗤之以鼻。但不幸的是,它们没被发现的概率太小了。错误会被发现。需要新的功能,新系统发布了。现在你不得不面对这堆恐怖的代码,试着去清理它们。这篇文章为这种不幸的情况提供了一些建议。

0. 值得清理么?

第一件你需要问问自己的事情就是代码值得清理么。我不是说当问到是否要清理代码时,你一定要回答是或者一定回答不是。是你对代码负有责任,也是你需要一直面对它们直到最终写出的代码是你乐意维护的,也是你很自豪的放入代码库的。

如果你觉得就算代码看起来很可怕,也不值得浪费你本来就很紧张的时间来修复它们。所以你仅仅做了最最微小的调整解救燃眉之急。

换句话说,你也可以将代码看作自己的,也可以看作是别人的。

两种情况都有优缺点。优秀的程序员看到烂代码时会觉得很难受。他们会拿出火把和叉子并且高呼:“太乱了,太乱了”。这是一种优秀的品质。

但是清理代码是一个繁杂的工作。很容易就低估了时间。甚至有时候和从头开始写代码一样的耗时。并且短期并没有带来任何的短期效应。两个星期的时间清理代码并不会带来任何新的功能,但有可能引入一些新的错误。

另一方面,如果长时间不清理代码可能会带来灾难性的毁灭。混乱是代码的杀手。

所以,这并不是一个容易做出的决定。需要考虑一些事情:

● 你期望对这段代码做多少改变?你是希望仅仅修改这个小错误呢,还是这段代码还要使用多次,所以你希望将它“调教”的好些,并且加上新的功能。如果仅仅是修复一个错误,那么最好是别打草惊蛇。然而,如果这个模块你需要长期折腾的话,那么现在开始花点时间来清理它吧,之后会省掉很多烦恼。

● 你需要或者是你想引入上游的更新吗?它是一个正在开发当中的开源项目吗?如果是的话,并且你想做改变的是上游的代码,那么你不能对代码有大的改动否则当你每次pull代码的时候都会经历一场merge的噩梦。所以你需要做一个友好的团队合作者,接受这个错误,将带有你修正的代码补丁发给代码的维护者。

● 要做多少工作?你一天内实际上能清理多少行代码?我们估计多于100行,少于1000行,好,我们假设是1000行。所以如果一个模块有30,000行代码的话,你可能需要一个月的时间。你有那么多时间吗?值得这么做么?

● 它是你核心的功能吗?如果这个模块只是边缘的模块,譬如字体渲染或者图像渲染,你可能并不在意它是否是乱七八糟的。你可能全盘不要,将来用另外的东西来代替,谁知道呢。如果这段代码关乎核心的性能,你需要慎重对待。

● 这段代码有多糟糕?如果代码仅仅有一点点糟糕,那么可能你还是可以忍受的。如果它是不可理喻的,令人崩溃的话,那么我们就必须对它下手了。

1. 建立测试用例

要认真清理一段代码意味着花一段时间来彻底清理它。你可能会毁坏它们。

如果你有一个比较好的测试用例,有一定的覆盖率,你将会很容易知道什么已经损坏了,并且你能够很快的知道你犯了什么愚蠢的错误。想要节省建立测试用例的时间在整个的清理代码的过程中是可笑的。建立测试用例吧。这是你第一件需要做的事情。

单元测试是最好的,但是所有的代码并不适应单元测试。如果单元测试过于繁琐,就换用集成测试吧。譬如,一个游戏关卡中需要一个人物完成一系列的动作和你清理的代码有关。

这样的测试更加耗时,所以不可能在每一次更改之后都测试一次,虽然这是最理想的情况。因为你将每一次改变都放到了版本控制系统中,所以情况还不是那么糟糕。所以每一段时间(比如,五个更改)就测试一次。当你发现了一个问题时,你可以通过二进制搜寻最近的几次commit中找到什么地方导致了问题的发生。

如果你发现了测试没有发现的问题,确保将这个也加入到测试中,以便将来可以测试它。

2. 使用代码版本控制系统

还有人需要被告知要使用代码版本控制系统吗?我希望没有。

清理工作是很关键的。你可能要做很多很多小的修改。如果什么地方出错了,你想回顾版本历史,你可能找到它错在哪。

如果你和我一样,你可能有时重构(清理愚蠢的类)的时候会出错,并且后来意识到这并不是个好的点子,或者这是个好点子,但是如果先做了什么之后所有的一切会变得更简单。所以你想快速的恢复一切到原状并且重新开始。

你的公司应该已经有代码控制系统了,你可以在不同的分支进行修改,在不打扰别人的情况下随意的commit。

就算情况不是这样的,你也应该使用版本控制。下载Mercurial(或Git),创建新的仓库,将代码从你们公司的愚蠢的系统中签出并放在这里。在库中commit你的更改。当你完成了之后你可以将所有的一切merge到那愚蠢的系统中。

拷贝库到一个代码控制系统中仅仅需要几分钟。很值得这么做。如果你不懂Mercurial,花一个小时学习它。你会为你这么做感到高兴的。如果你愿意的话,花30个小时学习下Git(我是开玩笑的!并不用这么久。现在是“nerd”战斗的时候了!)

3. 每次仅仅做一个小小的改动

有两种方法改进坏的代码:革命和改革。革命是用火把一切都烧掉,从新写一遍。改革是在不破坏的基础上每次只进行一点小小的改变。

这篇文章是关于改革的方法。我不是说革命的方法从来不是必要的。有时代码太糟糕了,需要用革命的方法。但是那些觉得改革的进度太慢的人们往往会鼓励改革,然而经常没有意识到问题的复杂性,并最终并没有比现存的系统更好。

Joel Spolsky写过一篇经典的文章,他没有掉入到这个紧张的争论的陷阱中。

改革的最好的方法就是一次只做一个小的改变,测试它,并且commit它。当一个改变很小时,它更容易理解改动的后果以及确保改动不会影响现有的功能。如果什么地方出错了,你仅仅需要核查很少的一部分代码。

如果你开始做更改并且意识到改得很糟糕,那么你恢复到上一次的commit,不会损失太多的无用功。如果你过了一段时间才发现什么地方有细微的差错,你可以在版本历史中使用二进制搜找到导致问题的更改。

最常见的错误就是一次进行多处更改。譬如,当去除不必要的类层次的势后,你发现API的方法并不是像你喜欢的使用方法,而你打算重新组织它们。不要这么做!先去除层次结构,commit之后再更改API。

聪明的程序员懂得组织,所以他们也不需要太聪明。

试着找一个途径,沿着这个途径你可以把代码变成你想要的模样,每次只有一点点改动。譬如,第一步你重命名方法,使之名字更合理。下一步,你可以将成员变量变成方法的参数。然后将算法变得更清楚些,等等。

如果你开始做更改,并且发现比你原先设想的改变要大,不要害怕又退回去,使用更小的更简单的步骤去完成同样的事情.。

4. 不要同时清理代码和修正代码

这是(3)的结果,但是仍然很重要。

这是一个常见的问题。你开始察看一个模块,是因为你想加入某个新功能。然后你发现这个代码相当的糟糕,所以你开始重新组织它并且加入新的功能。

问题在于清理代码和修正错误是完全不同的目标。当你清理的势后,你想让代码看起来更好,而没有改变它的功能。当你修正错误时, 你想改变功能。如果你同时清理代码和改正错误,很难保证清理不会改变什么。

先清理代码,然后再在一个干净的基础上,加入新的功能。

5. 删除你没有使用的功能

清理的时间正比于代码的数量,复杂性和糟糕的程度。

如果代码的功能你目前没有使用,而且在可预见的将来也不会使用,那么就删除它,这会减少你浏览的代码数,降低复杂度(删除不必要的概念和依赖)。你会清理的更快的,而且最后的结果会更简单。

不要留着代码仅仅因为“谁知道呢,你可能某一天需要它”。代码是有代价的 – 它需要被移植,修正错误,被阅读以及被理解。你有更少的代码,就更好。就算在最不可能的情况下,你需要这个旧代码,你也能从代码库中找到它。

6. 删除大部分的注释

烂代码很少会有好的注释。它们通常是这样的:

// Pointless:    // Set x to 3    x = 3;// Incomprehensible:    // Fix for CB (aug)    pos += vector3(0, -0.007, 0);// Sowing fear and doubt:    // Really we shouldn't be doing this    t = get_latest_time();// Downright lying:    // p cannot be NULL here    p->set_speed(0.7);

看看整个代码。如果一个注释对你来说不再有意义,也对你理解代码没什么帮助,那么就删除它。否则你只会浪费你的脑力去理解一堆对你理解代码没帮助的注释。

同样的删除那些已经被注释掉的代码。如果你还需要它的时候,它还在你的代码仓库中。

甚至如果注释是正确而且有用的,记住你还可以重构你的代码。可能当你完成重构后,这些注释不再正确了。这个世界上还没有一个单元测试能够告诉你注释是否已经损坏了。

好代码需要很少的注释因为代码自己已经自说明了而且很容易理解。拥有好名字的变量不需要注释去解释它们的用途。函数如果有好的输入输出,没有特殊情况时是不需要说明的。简单的写得很好的算法在没有注释的情况下也是容易理解的。而断言记录了条件和预测。

大部分情况下,最好的做法是删除所有旧的注释,专注于让代码变得干净和具有可读性,然后再在需要的地方添加代码 – 这些注释反应新的API的用途以及你对代码的理解。

7. 避免共享的可更改的状态

共享的可更改的状态是理解代码的最大阻碍,因为它允许隔一段距离的行动,一段代码可以改变另一段完全不同的代码的行为。人们常说多线程是困难的。事实上,是由于线程共享了可更改的状态,才导致了问题。如果你能避免它们的话,多线程并不复杂。

如果你的目标是写高性能的软件,你应该不能避免一切可更改的状态,但是你的代码仍然可以从减少它而获益。为了“大部分功能完善”而努力吧,确保你确切的知道什么状态在什么地方改变了,并且知道原因。

共享的可更改的状态来自不同的地方:

● 全局变量。最经典的例子。现在每个人都知道全局变量的坏处。但是要注意(有时人们会忘记),全局变量是唯一的会造成问题的共享的可更改状态。全局常量并不糟糕,Sprintf也不糟糕。

● 对象 – 装有乐趣的大袋子。对象能够集合很多方法,无疑可以共享很多可变的状态(成员)。如果一个懒惰的程序员需要将一些信息在方法之间传递的话,她可以建立一个新成员,所以可以依照需要来读它和写它。这非常像全局变量。多么有意思!当一个对象有越来越多的成员时,问题就越来越严重。

● 巨大的函数。你可能已经听说它们了。这种神秘的产物栖息在最黑暗的代码洞穴的最底层。心眼坏的程序员在阴暗的酒吧里谈论它们,他们的理智被他们遇见的代码摧毁了:“我不停地向下翻向下翻,我不能相信自己的眼睛。居然有12,000行。”当函数足够长的时候,它们本地变量将和全局变量一样糟糕。我们不可能知道改变2000行之后的一个局部变量会有什么效果。

● 引用和指针参数。引用和指针参数没有被声明为const被传进函数时,可以在被调用者,调用者以及任何能被传递相同的指针的对象之间充当共享的可变的状态。

这里有一些避免共享的可更改的状态的建议:

  • 将较大的函数切分成较小的函数。
  • 将较大的对象切分成较小的变量,将相关的成员放在一起。
  • 将成员变成private。
  • 将函数声明const,返回结果,而不是可更改的状态。
  • 将函数声明static,从参数获得值,而不是从共享状态那里取值。
  • 避免完全使用对象,实现纯净的功能,不要引入副作用。
  • 将本地变量声明const。
  • 将指针和引用声明const。

8. 避免不必要的复杂性

不必要的复杂性通常是过度工程化的结果 – 支持的结构(如序列化,引用计数器,虚拟接口,抽象工厂,访问者等等)会拖慢真正有实际功能的代码。

有时候过工程化是因为一些项目开始的时候有一些更大的野心,多于实际完成的。更多的情况,我想是因为程序员读了关于设计模式的书之后和瀑布模型之后的想法,他认为过工程化会形成更“坚固”和“高质量”的产品。

通常,这个笨重的,僵化的,过度复杂的模型不能适应功能需求,而这是设计师不期望的。那些功能可能之后用hack的方式来实现,成了在象牙塔最顶上的螺栓和后门,变成了神经错乱的混合结构。

治愈过度工程化的方法就是YAGNI(you are not gonna need it)-你不需要它!只有当需要一个东西的时候才建造它。当你需要它的时候才建立更复杂的东西,而不是在你需要之前。

避免不必要的复杂性的一些实际的方法:

  • 移除你没有用到的东西(就像上面建议的一样)。
  • 简化必要的概念,避免不必要的概念。
  • 移除不必要的抽象,用实际的实现来替代。
  • 移除不必要的虚拟化,并且简化对象的结构。
  • 如果一个设置曾经使用过,那么就避免在用另外的配置来运行这个模块。

9. 就这么多了

现在开始清理你的“房间”吧!

来自:伯乐在线

Dropbox推独立应用,公司估值已达100亿美元 【走近院士】梅宏:云计算时代软件技术发展需求和挑战 云计算战争:OpenStack vs. VMware Cortana与Siri、Google Now的较量:支持功能更多 代码面试最常用的10大算法 “颠覆医疗” 时云医疗推三款硬件产品 华为章宇:如何学习开源项目及Ceph的浅析 EMC:大数据时代中对于数据保护的重新定义 Google Glass于4月15日在美对外开放购买,售价为1500美元 电脑上的网址怎么样复制到手机 创客天下——《Make》及Maker Faire创办人、O&amp;#039;Reilly Media创始人Dale Dougherty专访 《近匠》aGlass团队:透析眼控技术的价值 一周消息树:超级充电器来袭,30秒可为手机充满电 北森CTO张庆化:利用Spark实现数据驱动价值 大公网冯咀志:站内搜索系统的PowerLinux迁移实践 2014移动开发者必备的十大应用测试工具 前《连线》主编Chris Anderson:创客就要DIT UC多屏战略 推出电脑版和电视版浏览器 “全国云计算大数据创新项目评选”征集活动正式启动 【走近院士】李伯虎:云制造技术的应用及发展 一周热点: Twitter的下一代分布式数据库Manhattan 专访微软陆奇:大转型中最难的是文化的改变 【CTO俱乐部走进腾讯】问题征集进行中 颠覆移动营销世界的女王Yunha Kim为女性CEO正名 Google揭露“仙女座”虚拟网络,及分散和集中架构之辩 DHTMLX Touch:开源的HTML5和JavaScript框架 Supercell下一城:《海岛奇兵》续写芬兰手游传奇 开发者盛宴 2014 Unity亚洲开发者大会盛大开幕 微软全球中国云业务总经理:云落地中国的最大难题 PTVS 2.1 Beta版发布 Visual Studio的Python工具 【走近院士】李兰娟:云计算与大数据将成为医疗改革利器 誰有Java的在線网絡游戲,就象263,yahoo上那樣的. error C2664是怎么回事?十万火急!!! 我的编译器总出 LINK : fatal error LNK1104: cannot open file "Debug/Typer.exe" 是什么原因? 送分喽 vb中的问题!!!急 请问:fastreport的自定义变量如何与delphi变量进行互相传递!!! 请问各位高手关于局域网共享上网的设置问题。 basic 的数据结构问题 asp与xml 百分求维护经验 初级问题 求救:如何用fileup组件将一个文件同时存到两台机子的硬盘上! 有关netmeeting、AVPhone或其它做视频会议相关控件的使用方法。 a question about String Type!Please help me! 关于%include的问题。高分 vb如何访问sql-server。 有用SDK编写的象“我的电脑”那样的程序吗? 请教:如何绘制成绩分析的正态分布曲线? bean里删除文件? 请问关于响应事件的问题?? 请问怎样用在Active DLL中调用ASP的response对象? 请介绍几本关于vb的书,不是入门的书,有没有向<delphi5开发人员指南》这样的书???? dos下编程,如何获得文件创建时间? 如何判断存储过程执行成功结束?(50分相送) 谁行行好吧,小弟谢过了!(sql的cd key) 请教现在是应该学习VB6还是VB.NET? 关于在mfc中自定义类的一个问题 请教现在是应该学习VB6还是VB.NET? 谁有在VB中开发DirectX的教程,请推荐下载地址. zrong(zr) TommyTong(童童) GreenWaterBlueSky(我的家在碧水蓝天) 进来看一看可以吗? 谁行行好吧,小弟谢过了!(sql的cd key) 这里是个关于Iczelion的win32的汇编教程中的一个问题,请各位指点! 请问如何让VB。NET编译的。EXE文件在没有装有。NET架构的系统上运行? 大白鲨:计算机软件专业到低学那些课程 窗口的最小化和最大化事件是什么? 怎样实现在主窗体显示后,出现登录窗口?? 三层结构系统的问题! 存储过程问题 如何从Select的结果中再查询数据? 求助:此书<<windows me程序设计手册>>附带光盘的源代码---作者台湾,位元文化 大白鲨:程序员的爱情问题 form1.WebBrowser1.Navigate('www.zz.ha.cn');showmessage('下载完成');总错误 提问:关于报时程序 如何用图像来显示一个灰度值矩阵? 终于下载完了77M的,高兴,爽! 树型控件中,我在itmexpanding 事件中写了代码,第一次展开树正常,但以后每展开一次,数据内容就会在原内容基础上增加,书上说必须清除 tab控件中的按钮怎样关闭对话框? 大白鲨:数据库查询进度 啥是message map 在哪儿啊?〉 请帮我解决用dreamweaver来做网页时令我头痛的一个问题?谢谢! 续:关于报时程序 最后一头战象 父与子阅读题答案 英语六级华研听力1000题的难度怎么样,比六级真题难还是简单 孟子和荀子都是战国人物,为什么他们会有不同的观点?孟子处于战国早期,荀子处于战国末期,他们的时代环境是相似的,但孟子提出了人性本善的政治观点,荀子提出了人性本恶的观点,这是为什 小海马为什么是父亲生?小海马为什么是父亲生的①温暖的海底,尤其是浅海区域,分外光明灿烂.那里有胜过陆地森林草原的海底植物,有嬉游其间形形色色的动物.生长在海底的红、白珊瑚,像庭 在哪能看到 英语六级答案? 荀子、孟子和孔子的思想有何异同?请各用一个名句来概括他们的突出特点. 孔子、孟子 、荀子、黄宗羲的民本思想是什么,董仲舒的民本思想主要体现在? 在政治经济学中,G表示金全,W表示商品,P是指什么呢,是利润,还是价格? 孔子和孟子 荀子 有什么不同? “性恶论”最早是由( )提出的 A.孟子 B.孔子 C.董仲舒 D.荀子 男生长得美是啥意思?长得像女生的男生被人说可爱是啥意思? 用‘my father past life'写一篇初一的英语作文 穷则独善其身,达则兼济天下的含义? Mygirl的中文意思是什么? my father is day 英语作文 中国古代“自然人”假设的提出人是( ) A.孔子和孟子 B.老子和庄子 C.荀子和墨子 D.世硕和董仲舒 如果有男生说你长得可爱,是什么意思啊?我同桌在有天上语文课的时候就问我:有没有男生说你长得可爱?我说没有(其实有一个),结果他却说:没有?那我来说,你长得很可爱!(谁能告诉我, 六级财富值是多少? “穷则独善其身,达则兼济天下. 鲁迅生平简介稍微短一点, 一道初三英语题,希望有讲解谢谢,急在线等As we all know, wood and stones ______ bridges in the past.A) used to build B) used to building C) were used to build D) were used to building 需要多少财富值才能升到六级. 达则廉济天下,穷则独善其身的意思 Jim found nobody in the classroom —— A.did heB.did Jim 我知道选A,但我想知道为什么不选B.这种从句不是应该看主句吗? 财富值是多少时是六级 My father 初一水平英语作文70词左右、越简单越好、快!!! 《最后一头战象》里的战象,在死做了什么事《最后一头战象》里的战象,在它死之前,做了什么事,请问下,3Q 百度知道六级财富值是多少? 求解数学题,快19:00前某车间缺勤人数是出勤人数的十分之一,后来又有2人因事请假,这时缺勤人数是出勤人数的八分之一,全车间有多少人?长方形砖的长为40厘米,宽为26厘米,用这种砖铺成一块 《最后一头战象》 嘎羧死前做了那些事 在现代市场经济体制中,政府越来越多的运用税收,利率等经济杠杆调节经济运行,经济杠杆能起到调节作用原因答案是直接关系着市场主体利益, 一条数学题(最好在今天早上7:00之前回答)用一副三角尺可以拼成的角是多少度?(本人不明白这道题的意思最好说说这题的意思!)(1)100度(2)105度(3)110度 最后一头战象讲了一件什么事? 利率是一国政府调控宏观经济的重要杠杆.变动利率对经济的影响的一般规律( )a.提高利率,储蓄减少.b.降低利率,投资会减少.c.提高利率,会抑制通货膨胀.d.降低利率,消费会减少.我选C.Ps:请 62页答案,别让我自己做,谁给我我给100财富币 请问《最后一头战象》中的象死前有何感想?作文 兼济天下和独善其身的意思不要达则兼济天下,穷则独善其身的解释.单独的解释, 有回答者必给财富! 最后一头战象嘎羧死前看到了什么?想到了什么? 兼济天下、独善其身的意思兼济天下、独善其身两个词语的含义,最好是自己的理解,只要词语的意思!不要什么“穷则”,“达则”,而且要是褒义 请问政治经济学中G-W-G'是什么意思 利率是如何作用的作用?请从宏观角度回答 独善其身 在经济学中G-G'什么意思?谢谢 杠杆的作用是什么? 百度知道财富值多少才升到五级?六级?七级?八级?九级?最高到什么境界? 政治经济学中,G-W-G'中的意思?G是资本,W是商品,分别是哪些英文单词呢? 作者写《最后一头战象》当时的时代背景 有一列数,这样排列;1,22,333,4444,55555,666666,7777777······第二百个数是什么?讲得清楚点说错了,是1,3······ 政治经济生活中的W-G W-GW 最后一头战象表达了作者怎样的感情请勿说一大堆。 在足球比赛中前11场比赛中,某队仅负一场,共积22分.按比赛规则,胜利一场得3分,平一场得1分,负一场得0分,则该队共胜了几场? 你回答的真多!我才1000多财富. 最后一头战象作者所表达的情感?急. 兼济天下,独善其身是什么意思?五年级下册的苏教版语文练习册上的单元测试3中的一题. 有人说~爱情一千个人有一千个答案~我想知道这一千个答案都是什么~ 最后一头战象的简介 谁能解释以下句子的完整意思呀:穷则独善其身.达则兼善天下 穷则兼济天下,达则独善其身 英语6级答案哪里有
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘