老码农:如何写出让自己满意的代码

标签: 码农 代码 | 发表时间:2014-04-04 08:36 | 作者:
出处:http://kb.cnblogs.com/

  今天有位朋友在微博上问我这样一个问题:

  “ @老码农的自留地 ,最近出于学习目的写一个管理系统,越到后边,越觉得自己前边的代码写得烂。前辈,我想让代码写得更好一点,能不能谈谈你的经验,给我指点一下!”

  我在回复里刚写了几句,就意识到140个字很难把我的想法说清楚,本着知无不言言无不尽的好为人师精神,我决定把我的回答写成一篇博文。

  首先要说明的是,我写这篇博客并不代表本人觉得自己的代码写得有多好。事实上我很清楚自己的水平,作为一个做应用系统的程序员,和那些做框架做系统的大牛根本就不在一个层次。而且即使在应用层次,我的水平大概也只能算二三流,只是因为热爱编程所以一直在努力而已,但不管怎么说,能做自己喜欢的工作我已经很满足了。所以我稍微篡改了一下问题,针对“觉得自己前边的代码写得烂”这个重点,把这位朋友问的如何“让代码写得更好一点”改成了“如何写出让自己满意的代码”。

  言归正传,我自己的体会是写代码很像写作文,开始写之前的构思过程是最关键的。记得高中的时候,有位语文老师给我传授的经验是,至少花三分之一的时间来构思,反复斟酌中心思想、各个段落的大意,文章的脉络,主要的修辞手法,等等。把这些要素都想清楚了,写起来就可以一气呵成。

  我觉得写代码也是一样,思路是最关键的。假定采用的技术平台、框架、工具等已经确定了,那么在开始动手写之前,花三分之一以上的开发时间去把所有的数据结构及其相互关系考虑清楚。例如需要定义几个类,类和类之间的关系是怎样的,每个类里都有什么属性,每个类提供一些什么样的方法,等等,这些是最核心的。这些数据结构要考虑得尽可能细,比如功能实现可能没问题,但是性能上不理想,这就说明你的数据结构设计还需要改进。这些细节要反复考虑,交叉检验,直到自己觉得很周到了为止。在此基础上,再注意实现的细节、测试用例、代码可读性,就应该可以写出让自己满意的代码。具体说明如下:

  1. 数据结构和核心算法

  关于数据结构的重要性,大神Linus Torvalds讲过这样的话,我觉得非常赞同:”Bad programmers worry about the code. Good programmers worry about data structures and their relationships.” (低水平程序员总在考虑代码,高水平程序员总在考虑数据结构及其之间的关系)

  数据结构考虑清楚了,核心的算法自然就出来了,这就是关于每个类的每个方法如何实现的问题。比如需要实现一个中位数查询方法,如果你前面确定了数据保存的格式是一个列表,那么你可以考虑采用插入排序法;如果数据格式是自平衡二叉排序树(AVL),则只需直接返回根节点就可以了。

  数据结构决定算法,所以你在考虑数据结构的时候,一定要尽可能地使数据的结构和它的自然属性相匹配,不然后面的实现就会是一场噩梦。比如,你把一个多层级的结构定义成二维数组,看上去也靠谱,相当于在一个表格里维护一个组织结构图,可是当你做到部门增减的时候,本层级的数组元素移动自不必说,下面各个层级的元素移动就很容易乱套,而且性能很差,可能你写了2000行代码还有很多边界条件会出错。相反,如果用一个孩子兄弟链表来表示这个树型结构,操作起来就非常容易,可能100行都足够了。

  2. 功能实现

  思路确定后,实现过程也需要大量的构思活动。碰到你比较熟悉有经验的领域,你自然可以轻车熟路,但难免会有一些你不太熟悉的技术需要尝试。有的同学比较排斥这种领域,比如我好不容易才掌握了Struts 2,领导又让我去学习Grails框架,我就会觉得很不爽,大概看了看就挑出它的一堆问题,然后能躲多远就躲多远。可是我要说,这样的心态会阻碍自己不断提高技术水平。作为一个程序员,最大的挑战也是最大的乐趣所在,就是不断学习新的技术,没有这样的心态,很快就会落后。

  好,那么遇到不熟悉的技术怎么办?我的体会是,先不要急着实现项目中的代码,自己另外维护一个测试项目,在里边边查文档边学习,边做一个小功能,把所有需要在项目中实现的功能先在测试项目里跑通,然后再写项目里的代码。这样做的好处是把单个技术问题和其他潜在的bug隔离开来,便于快速学习新技术。否则,你直接在项目里写代码出错以后,要判断问题的源头都要多费好几倍的精力。

  3. 测试

  测试很重要,设计测试用例就像开发时设计数据结构一样,也是很关键的。在设计测试用例的时候,要把当时自己设计数据结构的思路全部忘掉,或者找别人来设计测试用例,不然会不由自主地测试那些你已经考虑到了的地方。这样测试是跑通了,用户一用起来可能各种边界条件会到处出问题。

  有人会推崇TDD的方法,先设计好测试用例,然后在开发过程中确保所有测试通过。我个人不喜欢这种方法,虽然承认从开发质量管理和长期维护的角度来说TDD是很有必要的,但我个人尝试的结果是,设计完测试用例后,想到开发的目标不是实现功能,而是为了跑通测试,就感到毫无乐趣可言。这一点我自己也觉得很矛盾。

  写到这里我又想到大神Linus说过的另一句话:”Regression testing” What’s that If it compiles, it is good; if it boots up, it is perfect. (“回归测试”?这是什么东西?如果代码能编译就是好的,如果它启动了,那就是完美的。)
  当然了,大神水平摆在那里,他有资本目空一切,咱确实没资格仿效。但是我还是觉得TDD也有TDD的问题,测试是很重要,但把它摆到驱动开发的高度,就有点本末倒置了。这个是我自己的一点看法,本人对TDD了解得不深入,如果有谬误之处,请多多指教。

  4. 代码可读性

  要想自己满意,代码的可读性一定要好。要做到一年后甚至几年后你拿到自己写的代码,还能很容易看明白当时的思路和实现。这就涉及到命名和注释的问题。

  命名就像超市里的商品标签一样,要让看得人一目了然就知道这是个什么东西,比如你在员工类里定义了两个属性是到岗日期和离职日期,定义成date1和date2就没有多少可读性,定义成dateOnBoard和dateQuit就比较清晰一些。

  注释也是很重要的,它可以用来说明一段代码的作用,算法的设计思想,或者是方法调用的参数格式要求等。有人觉得命名就是注释,代码本身就为自己代言了。我觉得这种说法用来强调命名规范的重要性是很好的,但是因此说不需要注释则有失偏颇。试想,如果Dijkstra首次发明最短路径算法的时候,他给出的代码里没有一行注释,即使所有的变量命名都定义得准确而严谨,又有几个人能看懂他的算法呢?所以,在重要或者复杂的地方,都需要详细地写一些注释,便于看代码的人清晰地了解你的思路。

  最后总结一下:要想写出自己满意的代码,首先不要急于动手,要先仔细想清楚思路性的东西,尤其是数据结构,然后在实现过程中大胆尝试小心验证,设计好测试用例,确保代码的可读性,就可以在代码中表现出自己的最高水平。但毕竟各人水平是有差异的,自己满意并不等于其他人欣赏。我对此的看法是,不求尽如人意,但求无愧我心,足矣。最后再啰嗦一句,技术水平是可以慢慢提高的,但是好的编程习惯需要从一开始就养成,它会让你在前进的道路上事半功倍,受益终生。

相关 [码农 代码] 推荐:

老码农:如何写出让自己满意的代码

- - 博客园_知识库
  今天有位朋友在微博上问我这样一个问题:.   “ @老码农的自留地 ,最近出于学习目的写一个管理系统,越到后边,越觉得自己前边的代码写得烂. 前辈,我想让代码写得更好一点,能不能谈谈你的经验,给我指点一下.   我在回复里刚写了几句,就意识到140个字很难把我的想法说清楚,本着知无不言言无不尽的好为人师精神,我决定把我的回答写成一篇博文.

我的码农原则

- - 钛媒体TMTpost—把脉科技资本论
这篇文章只是体现我以前写代码和做代码审查时候的一些原则. 正确性 (Correctness). 结构 (Code Structure). 第一步,第二步;需要什么数据,需要做什么处理,处理完了结果到那里去,都应该在结构中被很好的体现出来. 我的经验来看,一般来说,在design chart上面的每个component都对应着自己的class,然后之间或class内部的通信通过member function来完成.

代码重构

- - ITeye博客
随着程序的演化,我们有必要重新思考早先的决策,并重写部分代码. 代码需要演化;它不是静态的事物. 重写、重做和重新架构代码合起来,称为重构.    当你遇到绊脚石  ---  代码不在合适,你注意到有两样东西其实应该合并或是其他任何对你来说是"错误"的东西  -------- . 如果代码具备以下特征,你都应该考虑重构代码:.

代码小比较

- Tim - 斯巴达第二季
判断上百万个4k的buffer是否为全0,我最先想到的办法是:zero_buffer = malloc(4096);. /* 循环百万次读取buffer */.         /* 全0 */. 由于好奇,看看shell工具cp的代码,它的解决办法是:. /* 循环百万次读取buffer */.         /* 全0 */.

两行 JavaScript 代码

- MessyCS - Dreamer's Blog
最近看到了两行 JavaScript 代码,很受启发. 在 JavaScript 中,我们可以获取HTML元素的属性值,例如 element.id. 但是,因为 for 和 class 是 JavaScript 中的关键字,所以在 JavaScript 中这两个属性名称分别用 htmlFor 和 className 代替,于是在封装的时候需要先对这两个属性进行特殊判断.

Netty代码分析

- LightingMan - 淘宝JAVA中间件团队博客
Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序[官方定义],整体来看其包含了以下内容:1.提供了丰富的协议编解码支持,2.实现自有的buffer系统,减少复制所带来的消耗,3.整套channel的实现,4.基于事件的过程流转以及完整的网络事件响应与扩展,5.丰富的example.

python代码调试

- - 阿里古古
【转自: http://blog.csdn.net/luckeryin/article/details/4477233】. 本文讨论在没有方便的IDE工具可用的情况下,使用pdb调试python程序. 例如,有模拟税收计算的程序:. debug_demo函数计算4500的入账所需的税收. 在需要插入断点的地方,加入红色部分代码:如果_DEBUG值为True,则在该处开始调试(加入_DEBUG的原因是为了方便打开/关闭调试).

ios代码开源

- - CSDN博客移动开发推荐文章
本人从10年开始搞ios开发,从菜鸟到现在的入门,期间遇到了许多困难,也总结了一些东西,本着开源精神,希望大家共同成长的目的把这个工程开源出来.. 这个工程是从11年到13年之前完成的.主要是我平时用到的一些基础功能模块.其中有其他开源的代码和我自己写的一些.代码结构基本乱,12年以后的代码结构还可以,不是很乱,之前水平有限,如果不怎么样就别喷我了.

Oracle错误代码

- - 数据库 - ITeye博客
ORA-00001: 违反唯一约束条件 (.). ORA-00017: 请求会话以设置跟踪事件. ORA-00018: 超出最大会话数. ORA-00019: 超出最大会话许可数. ORA-00020: 超出最大进程数 (). ORA-00021: 会话附属于其它某些进程;无法转换会话. ORA-00022: 无效的会话 ID;访问被拒绝.

Java代码优化

- - ImportNew
2016年3月修改,结合自己的工作和平时学习的体验重新谈一下为什么要进行代码优化. 在修改之前,我的说法是这样的:. 就像鲸鱼吃虾米一样,也许吃一个两个虾米对于鲸鱼来说作用不大,但是吃的虾米多了,鲸鱼自然饱了. 代码优化一样,也许一个两个的优化,对于提升代码的运行效率意义不大,但是只要处处都能注意代码优化,总体来说对于提升代码的运行效率就很有用了.