当前位置: 首页 > 科技观察

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

时间:2023-03-19 14:13:37 科技观察

今天有朋友在微博上问我这个问题:“@老码农的自己地,最近为了学习写了一个管理系统,越发觉得前面写的代码不好,前辈,我想把代码写的更好,能不能谈谈你的经验,指点一下!我只是在回信中写了几句话,才意识到用140个字很难表达清楚我的想法。本着做一个无所不知的好老师的精神,我决定把我的答案写成博文。首先要说明的是,我写这篇博客并不代表我认为我的代码写得有多好。其实我很清楚自己的水平。作为一个做应用系统的程序员,我和做框架、做系统的不是一个层次的。而且即使在应用层面,我的水平也只能算是二流或者三流,只是因为喜欢编程所以一直在努力,但不管怎么说,能做自己喜欢的工作已经很满足了.于是对题做了一点改动,针对“感觉前面写的代码不好”这个重点,把朋友的题“怎么写代码更好”改成了“怎么写”令我满意的代码”。言归正传,我自己的体会是写代码很像作文,开始写之前的构思过程是最关键的。记得高中的时候,一位语文老师教我的经验是,至少要花三分之一的时间构思,反复考虑中心思想,每段大意,文章的上下文,主要修辞手法等。一旦你把这些要素想清楚了,你就可以一口气写出来。我觉得写代码也是一样,思路最关键。假设采用的技术平台、框架、工具等都已经确定,在开始手写之前,花三分之一以上的开发时间把所有的数据结构和它们之间的相互关系都考虑清楚。比如需要定义几个类,类与类之间的关系是什么,每个类有什么属性,每个类提供什么方法??等等,这些才是核心。应尽可能详细地考虑这些数据结构。比如功能实现可能没问题,但是性能不理想,这说明你的数据结构设计还需要改进。这些细节要反复考虑,反复核对,直到觉得很周到。在此基础上,注意实现细节、测试用例、代码可读性,应该能写出令自己满意的代码。具体描述如下:1.数据结构和核心算法关于数据结构的重要性,LinusTorvalds大神是这样说的,我觉得我很赞同:“坏程序员担心代码,好程序员担心数据”结构及其关系。”(低级程序员想的永远是代码,高级程序员想的永远是数据结构和它们之间的关系。)数据结构想清楚了,核心算法自然就出来了。这是关于每个类的每个方法如何去做的问题。例如,您需要实现一个中值查询方法。如果已经确定数据存储格式是列表,那么可以考虑使用插入排序的方法;如果数据格式是自平衡二叉排序树(AVL),只需要直接回到根节点即可。数据结构决定了算法,所以在考虑数据结构的时候,一定要尽可能让数据结构与其自然属性相匹配,否则后续的实现将是一场噩梦。比如你把一个多级结构定义成一个二维数组,看起来靠谱,相当于在表格里维护了一张组织结构图,但是当你增减部门时,这一层的数组元素会移动不用说,下面每一层的元素移动很容易乱,性能很差。也许你已经写了2000行代码,有很多边界条件会出错。相反,如果用一个子弟列表来表示这个树结构,就非常好操作,可能100行就够了。2、功能实现的思路确定后,实现过程还需要进行大量的构思活动。遇到自己比较熟悉和经验丰富的领域,自然可以熟悉,但难免会有一些自己不熟悉的技术需要去尝试。有些学生对这个领域很反感。比如我终于掌握了Struts2,领导让我学习Grails框架。尽可能隐藏。但是我想说,这样的心态会阻碍自己不断提升自己的技术水平。作为程序员,最大的挑战也是最大的快乐,就是不断学习新技术。没有这种心态,你很快就会落后。好吧,那么当你遇到不熟悉的技术时你会怎么做?我的经验是,不要急于实现项目中的代码,自己单独维护一个测试项目,一边做一个小功能一边查文档学习,把所有需要实现的功能都放在project在测试项目中先跑通,然后在项目中编写代码。这样做的好处是可以将单个技术问题与其他潜在的bug隔离开来,便于快速学习新技术。否则,你直接在项目中写代码出错后,要花好几倍的精力去确定问题的根源。3.测试测试非常重要,在开发中设计测试用例与设计数据结构一样重要。在设计测试用例的时候,应该忘掉当时自己设计数据结构的所有思路,或者找别人设计测试用例,否则会不由自主地去测试已经考虑过的地方。就这样,测试跑通了,用户一旦使用,可能会遇到各种边界条件的问题。有些人会提倡TDD方法,先设计测试用例,然后在开发过程中保证所有测试通过。我个人不喜欢这种方法。虽然从开发质量管理和长期维护的角度来说,我承认TDD是必要的,但我个人尝试的结果是,在设计完测试用例后,我认为开发的目标并不是实现功能。但是为了跑通考试,感觉一点乐趣都没有。我对此也感到矛盾。写到这里,我又想到了Linus说过的另外一句话:“回归测试”What'sthat如果编译通过,那很好;如果它启动了,那就完美了。(“回归测试”?这是什么东西?如果代码能通过编译就好了,能启动就完美了。)当然,高手的水平摆在那里,他有资本看一切,而我们真的没有资格跟风。但是我还是觉得TDD也有TDD的问题。测试很重要,但是放到驱动开发的层面就有点颠倒了。这是我自己的观点。我对TDD没有很深的了解。如有谬误,请指教。4.代码可读性要想满意,代码的可读性一定要好。一年甚至几年后,当你拿到自己写的代码时,很容易理解当时的思路和实现方式。这就涉及到命名和注释的问题。命名就像是超市里的商品标签,让人一看就知道是什么东西。比如你的employee类中有两个属性,分别是到达日期和离职日期。将它们定义为date1和date2。可读性不高,定义dateOnBoard和dateQuit比较清晰。评论也很重要。它们可以用来解释一段代码的功能,一个算法的设计思想,或者一个方法调用的参数格式要求。有人认为命名就是注释,代码本身会说话。我认为这种说法是用来强调命名约定的重要性,但说不需要注释是有失偏颇的。试想,如果Dijkstra首先发明了最短路径算法,他给出的代码中没有任何注释,即使所有的变量名都定义得准确、严谨,又有多少人能看懂他的算法呢?所以,在重要或者复杂的地方,需要详细的写一些注释,让看代码的人清楚的明白你的思路。最后总结一下:如果你想写出自己满意的代码,首先不要急着去做。思想上的东西,尤其是数据结构,你首先要认真思考,然后在实现过程中大胆尝试仔细验证,设计测试用例,保证代码的可读性,才能在你的代码中表现出你自己的最高境界。但毕竟每个人的水平不同,自己的满意不代表别人欣赏。我对此的看法是,不完美就够了,只要对得起自己的心。最后说一句,技术水平可以慢慢提高,但是良好的编程习惯需要从一开始就养成,它会让你在前进的道路上事半功倍,受益终生。