人,多是无知与自大的,我也不例外。自认为挺聪明,也好学习,在编程上也有热爱之情。因此早已预设自己未来于编程道路上定有一番作为。还好,好奇心与读书拯救了深陷幻想中的我。顶级大神如何编程?有什么经验之谈?有哪些书籍能醍醐灌顶,影响程序员一生?带着疑惑,上网搜索,找到了一份程序员必读书单。草草通读完《代码整洁之道》,《代码大全》,对编程有了新认知,也正视了自己。编程需要注重细节,要计划先行,谋定而后动;本人无非一枚普通人罢了。空想无益,自大较无知更可怕,想实现理想,必须勤勤恳恳,早早养成好习惯。
编程是一种技艺甚于科学的东西,要编写整洁代码,必须先编写脏代码,然后再清理它!
代码整洁
“离开时要比发现时更整洁”
好事多磨,不论是维护他人的代码,还是更改自己的代码,都要抱此代码一定有改进的空间的想法,而不非仅仅只为完成特定功能而已。这样一来,多思考,产生多种改进方案,最后迭代出最好的方案,一步一步的使代码更整洁。“Later equals never”
稍后等于永不。想着先实现功能,代码混乱点,复杂点,没关系,先能完成需求,通过测试吧。以后有时间,再回过头来好好梳理,重构代码,不是还使用 TODO 标记了吗?这样一来,也保证自己不会忘记。亲身验证,如果测试已通过,这代码肯定不会去管了,即使有空闲时间,甚至是出现 bug,也仅仅是在原先的基础上修改,根本不会,也没胆量魄力完全抛弃烂代码,重新梳理需求,重新设计、再编写。“花时间保持代码整洁,不但关乎效率,还关乎生存”
不整洁的代码,难以维护,且错误百出;不但拖累开发效率,而且代码质量差,bug 层出,修改成本高,也容易引入新 bug,会越改越差,直到无法掌控,必须废弃为止。为了公司的生存,也要多花些时间去保持代码整洁,公司凉了,自己不也凉凉吗?“做的快的唯一方法是保持代码整洁”
之前认为,先快速上手,编写代码,完成主要功能需求即可。反正需求会改的,先做个大概,以后再根据需求边做边改。需求都没完全确定,写的代码没自然没必要花费时间保持代码整洁,做封装、设计、模块化、写单元测试都是扯淡,多此一举。事实证明,全错了,没搞清楚需求,没设计,直接上手写代码,就是自掘坟墓,bug 多,难修改,又不想舍弃重写,极其难受,一次次的向烂代码妥协,一次次的放弃编写整洁代码的机会,如何成长为一名光荣而优秀的程序员。“逻辑直接了当,缺陷就难以隐藏;减少依赖,便于维护”
编写逻辑清晰代码,依赖自然会减少,缺陷也就越容易被发现,也就越好维护。如果觉得编写困难,意味着对问题的认知不足,需求不明,逻辑不清,是没法编写高质量的代码的。归根结底,还是要清楚的理解需求,分解问题,设计好,多思考,尽力能设计出多种方案,再比较方案优劣,从中选出最优解。“糟糕的代码引发混乱,别人修改时会越改越烂。所以,把代码调至最优,省的别人做没有规矩的优化,搞出一堆混乱来”
本人打心里不希望别人来改自己的代码,一来怕别人越改越乱,二来是怕别人与自己思路对不上,改变了我的思路与设想。如果,我不给别人修改代码的理由,不就能阻止吗?于一开始就考虑周达,做到代码整洁,性能最优。“使之易读,实际上也使之易写”
代码是给人读的,不管给自己,还是将来给别人读,如果不能保证代码的可读性,那么此代码将毫无意义,终将会被摒弃。与其花费时间精力去读一份混乱的代码,为何不抛弃它,自己重新设计,编写呢?多数程序员是喜欢编写新代码,极其讨厌维护他人代码的,原因就是如此,他人代码难以阅读。另外,如果能做到代码易读,侧面证明了该程序员明确需求,设计合理,逻辑清晰,写起来当然也容易。“如果名称(包括变量名、函数名、文件名、包名、模块名)需要注释来补出,则不算名副其实”
花时间在名称命名上,是值得的。清楚的、易懂、无歧义的命名,就代表了你对程序的理解。如果你取不出一个好的名字,则间接证明你对问题还不是很理解,设计的程序是存在的问题。
函数
“函数不应该大到足以容纳嵌套结构,因为 if else 或 switch 天生要做 N 件事”
“禁用标识参数,标识参数证明函数不止做一件事; true:事 1;false:事 2”
“每个函数一个抽象层级”
代码层级是自顶向下的,每个函数需要在一个抽象层级中。混合多层级内容的函数,依赖过多,需要顾虑的方面过多,难以编写测试代码,更不太容易复用,理解意图难、维护难。函数只做一件小事,控制在同一层级中,清楚表达其功能即可。“函数应该尽量避免多参数,如果函数需要多个参数,就说明其中一些参数应该封装为类或对象”
过多的参数,很可能是函数做的事情模糊不清,不止一件。过多的参数,在调用时需要注意参数顺序,数量,测试难,读懂难,复用难。“函数分 2 种:1.做什么事;2.回答什么事。二者不可兼得”
编写函数的目的,要么是希望它去做什么事,是一个指令;要是是希望它回答什么,有个返回值,是一个询问。要分隔询问与指令,例如打开文件函数,要先判断是否有该文件,有则打开。判断是否有文件是个询问,打开则是一条指令,二者要分开。需要 2 个函数完成,判断是否有文件、打开。先询问在下发指令。“函数取名,应是动宾词组,动词+关键名词;如果有参数,函数和参数应该形成一种动词+参数的对应形式,以解释函数的意图,以及参数的顺序意图”
例如writeFile(name)
,就比write(name)
更成解释函数意图,写入的是文件格式,名称是 name。assertEquals(expected,actual)
,你并不知道参数的顺序,哪个是实际值,哪个期待值.assertExpectedEqualsActual(expected,actual)
,函数名则说明了参数的顺序。“函数别返回 NULL 值,也别传递 NULL 值”
“对于第三方 api 要学会用装饰器模式管理,降低对它的依赖”
对于使用第三方的 api,最好使用装饰器模式管理,降低对其依赖,未来可轻松的改用其他代码库。
类
- “类名应该描述其权责,如果无法为某个类命名,则说明这个类有过多的权责”
- “每个小类封装一个权责,且只有一个修改它的理由”
- “依赖倒置:
- 1.高层次的模块不应该依赖于低层次模块,它们都应该依赖于抽象;
- 2.抽象不应该依赖于具体,具体应该依赖于抽象”
- “对象与数据结构是对应的”
对象把数据隐藏于抽象之后,暴露操作数据的函数;数据结构只暴露了某数据,但没有提供有意义的函数。
检查、测试
“在没有检查程序,并确保程序是正确之前,不要急于开始编译”
“千万不要陷入试图通过运行程序来检查其是否正确的怪圈”
学会审查自己,不光是在编码阶段,在编码前后、设计、测试时都要去检查。要对自己的代码负责,对自己有信心,做个踏实可靠的人。自己审查过的代码,一定是高质量的。千万不可把错误交个编译器、测试来检查,到那时再去修改,已经晚了。一是改动代码,牵扯关联的地方多,修改成本大;二来时间不多,都到测试阶段了,也没多少时间去好好设计,重构。“没有测试的代码,是不可测的代码,也就无法验证对错,也就不可部署”
“脏测试等于没测试,测试代码也要保持整洁”
不够整洁的代码,是不能确保测试质量,也更难以利用,代码变更,测试代码也需要做相应的调整。保证测试代码长久易懂,同样需要花时间做设计,保证代码整洁。“每一个测试,有一个断言,并只测一个概念”
测试用例也就是一个函数,只做一件小事。仅一个断言,测试难度小,需要测试路径清晰,数量少,能保证质量。“测试的三个环节,Build -> Operate -> Check”
- 构造测试数据
- 操作测试数据
- 检查操作正确性
“如果发现程序的错误异乎寻常的多,那就要重新开发,不要试图去修补它。修补意味着不充分理解,而且肯定会在将来产生更多的问题”
程序设计
- “程序设计是一个逐次迭代逼近的过程”
- “程序设计也是一个启发过程,需要吃一堑,长一智”
- “要把最可能改动的区域设计成最容易改动的区域;最容易改动的部分:”
- 硬件依赖部分
- 输入输出部分
- 复制的数据结构
- 商务规则
最深体会
- 想要成为优秀的程序员,写出高质量的代码。需要坚持编写代码、然后不厌其烦的一遍遍重构改进,同时还要克服自我劣性(懒惰,妥协将就、怕麻烦的心理)。
- 编写代码之前,做好设计,越详细越好,同时要不断的调整设计,逐步求精。
- 要养成良好的编程习惯,清晰明了的命名,只做一个小事的函数,逻辑明了,易读易懂的流程控制,必要的注释说明,编写详细的设计文档等等。好习惯是一个人的思想与行为的体现,习惯能减少思考时间,简化行动步骤与阻力,提高效率。
- 要建立自信心,营造踏实可靠的人设。我写的代码,提测后,bug 少;我完成需求,保量过硬;我不会迟到,我不吹牛,我不做无准备之事。