如何写出好代码?我发现如今鲜有软件工程师对这个话题有太多的兴趣,他们喜欢追捧最新的框架,喜欢每天去 LeetCode 打卡刷题,喜欢管中窥豹地背诵「八股文」,却忘了他们最应磨练的「手艺」:写代码。本文我将分享关于写出好代码的一些技巧和感想。
为什么要写好代码
每个稍具几年工作经验的软件工程师应该都不会有什么疑问,你在入行的这几年里,一定经手过这样一些项目,它们里面有看不懂的变量、看不懂的函数、看不懂的逻辑、奇怪的注释、纷繁复杂的嵌套等,让每一个接手项目的人都有很重的心理负担,每天上班前都要做很多心理建设。每次接手这个项目的新需求或者改动旧代码对你而言都是一件痛苦无比且风险极高的事,这是不注重代码质量的团队所必须承受的混乱的代价。
什么是好代码
我们先来界定一下什么是好代码。必须得承认的是,评判代码的好坏是一件非常主观的事情,每个人都可以有他自己的见解。我对好代码的标准可以用一句话概括,就是在可接受的范围内,复杂度越低越好。此外,好代码有一种共性,正如我标题所说,它们是一种艺术,跟绘画、电影、音乐一样,是一种代表美的事物,你在阅读它们时能够体会到它们的美感,能够感受到一种愉悦。你的审美能力越好,感受的强度也就越高。
如何写出好代码
这篇文章无意像其它列清单式的文章一样列出所有写出好代码的要点,我将通过一些经典的案例来让你感受这门控制复杂度的艺术。它们包含三个方面,命名、函数、注释。并不是说这门艺术只包含这三个方面,只是以我做 Code Review 的经验来说,做好这三个方面可以解决 90% 的问题。
命名
There are only two hard things in Computer Science: cache invalidation and naming things.
– Phil Karlton
计算机科学只存在两个难题:缓存失效和命名。当我在大二第一次听到这句名言的时候,我还以为它是在对写程序的过程中命名难的问题进行调侃,显然,我那时候还没有意识到命名对于写好代码的严肃性。
命名最难的地方在于需要良好的描述技巧和共有的文化背景。命名涉及的方面很多,比如变量名、函数名、类名、模块名、微服务名、项目名等,它们的名称在确立下来之时,就应该回答了大部分的问题。它该告诉你,它为什么会存在、它做什么事、应该怎么用。如果名称还需要额外的解释(注释),那它就不是一个名副其实的命名,你应该想想如何换个好命名。不要不舍得把时间花在命名上,它很重要。
例1 名副其实
1 |
|
类似于「d」这种毫无意义的变量,不如具体地描述变量本身,不要怕变量名称太长,长比短好。但也要注意不要在命名中出现废话,Variable 一词永远不应当出现在变量名中,Table 一词永远不应当出现在表名中。用 String 类型来表示名称时就叫「name」而不是类似于「nameString」的冗余废话。
例2 做有意义的区分
1 |
|
如果参数名改成 source 和 destination 就会好很多。
例3 添加有意义的语境
1 |
|
这是一个游戏中怪物属性的设计,当我们要对零散的变量进行操作时不如思考如何让他们聚合在一个有意义的语境里。
函数
例1 只做一件事
一个函数只应该做一件事。想象一下你正在准备一个晚餐,你需要做的事情有很多,包括准备材料,烹饪,设定餐桌,清洁和洗碗。如果你是一个函数,试图一次性做所有这些事情,那就可能会导致混乱和低效。
相反,如果你将每一个任务都视为一个独立的“函数”,把这些抽象层级抽离出来,就能更好地处理。比如,你可能会有以下的函数:
1 |
|
在这个例子中,每个函数都只负责一个任务,职责清晰。如果其中的某个步骤出现问题,比如烹饪过程中煮糊了饭,你就只需要修改“烹饪”这个函数,而不用改动其他部分。这使得问题的定位和修复变得更简单,也能提高整体的效率和生产力。
例2 函数参数
函数参数应该越少越好,参数多的函数不仅阅读困难,在写单元测试时也常常制造麻烦。一元参数应该尽量使用返回值,如果函数要对输入参数进行操作,好的做法是用返回值来接收修改结果,而不是直接修改输入参数。如果参数实在太多,应该使用对象来传入。
注释
关于注释,其实只有一条准则,这条准则对第一次听的人来说可能会有点难以理解,那就是尽量不写注释。
想象一下你什么时候会想写注释?这里逻辑太复杂了、这里做法太奇怪了不合常规、这个函数太难懂了等等,简而言之,就是你写的代码有问题,你应该尽量避免这种情况,而不是用注释来偷懒。注释越多的代码越烂。我甚至听过一些极端的技术团队禁止代码里出现注释。
团队 Code Review 的一些经验
我经历过几乎是开源代码级别严苛的 Code Review,每次无论是大是小的任何分支合并都要拉其它人 Code Review,团队严禁对自己发起的 PR 进行合并。每个新人都要读一遍《Clean Code》并进行心得分享,团队有一个共享文档维护沉淀的 Code Review 经验,每个月开一次会对近期 Code Review 产生的疑问进行讨论。可想而知这样会导致每次开发周期都会变得特别长,但是开发团队能争取到这样的话语空间,对技术人本身来说是受益无穷的,终于有机会可以对书本上的最佳实践进行实践了。
也许是受自己的经历影响,我觉得那种事后拉着组员一起 Review 的行为基本没用,业务重压之下,这种形式的 Review 不会持久、不会有效、有问题不会排期修改,再说了,很多问题如果一开始没有暴露出来,写好了再改是非常难的,毕竟:
1 |
|
前面说过,代码审美是一个非常主观的东西,没有最好的代码。团队 Review 里,由于这样那样的原因,人与人之间总会有冲突存在,通常来说,我们需要在团队的共识中得到小范围的最优解。
最后
写好代码是一件非常困难的事情,其实上面的一些准则和经验都是些非常简单易解的知识,但是要做到并且完全遵守非常困难,毕竟人最大的痛苦在于无法跨越知道和做到的鸿沟。如果你是团队分工的一部分,那么能不能写出好代码几乎取决于团队制定的守则,如果没有团队给你争取排期、制定 Code Review 规范、成员的代码意识,那么你一定会要花费足够的心力来维护你的代码洁癖,久而久之,难免滑向懒惰的深渊。
代码永远在变化,每次代码的修改,都可能在系统引起熵增,没有最好的代码,只有更好的代码。对好代码的追求应该永远在路上。