专栏名称: Cocoa开发者社区
CocoaChina苹果开发中文社区官方微信,提供教程资源、app推广营销、招聘、外包及培训信息、各类沙龙交流活动以及更多开发者服务。
目录
相关文章推荐
51好读  ›  专栏  ›  Cocoa开发者社区

关于烂代码的那些事——什么是好代码

Cocoa开发者社区  · 公众号  · ios  · 2017-04-07 18:04

正文

原文

摘要

这是烂代码系列的第二篇,在文章中我会跟大家讨论一下如何尽可能高效和客观的评价代码的优劣。

在发布了关于烂代码的那些事(上)之后,发现这篇文章竟然意外的很受欢迎,很多人也描(tu)述(cao)了各自代码中这样或者那样的问题。
最近部门在组织bootcamp,正好我负责培训代码质量部分,在培训课程中让大家花了不少时间去讨论、改进、完善自己的代码。虽然刚毕业的同学对于代码质量都很用心,但最终呈现出来的质量仍然没能达到“十分优秀”的程度。 究其原因,主要是不了解好的代码“应该”是什么样的。

什么是好代码

写代码的第一步是理解什么是好代码。在准备bootcamp的课程的时候,我就为这个问题犯了难,我尝试着用一些精确的定义区分出“优等品”、“良品”、“不良品”;但是在总结的过程中,关于“什么是好代码”的描述却大多没有可操作性

2.1.好代码的定义

随便从网上搜索了一下“优雅的代码”,找到了下面这样的定义:

Bjarne Stroustrup,C++之父:

  • 逻辑应该是清晰的,bug难以隐藏;

  • 依赖最少,易于维护;

  • 错误处理完全根据一个明确的策略;

  • 性能接近最佳化,避免代码混乱和无原则的优化;

  • 整洁的代码只做一件事

Grady Booch,《面向对象分析与设计》作者:

  • 整洁的代码是简单、直接的;

  • 整洁的代码,读起来像是一篇写得很好的散文;

  • 整洁的代码永远不 会掩盖设计者的意图,而是具有少量的抽象和清晰的控制行。

Michael Feathers,《修改代码的艺术》作者:

  • 整洁的代码看起来总是像很在乎代码质量的人写的;

  • 没有明显的需要改善的地方;

  • 代码的作者似乎考虑到了所有的事情。

看起来似乎说的都很有道理,可是实际评判的时候却难以参考,尤其是对于新人来说,如何理解“简单的、直接的代码”或者“没有明显的需要改善的地方”?

而实践过程中,很多同学也确实面对这种问题:对自己的代码总是处在一种心里不踏实的状态,或者是自己觉得很好了,但是却被其他人认为很烂,甚至有几次我和新同学因为代码质量的标准一连讨论好几天,却谁也说服不了谁:我们都坚持自己对于好代码的标准才是正确的。

在经历了无数次code review之后,我觉得这张图似乎总结的更好一些:

代码质量的评价标准某种意义上有点类似于文学作品,比如对小说的质量的评价主要来自于它的读者,由个体主观评价形成一个相对客观的评价。并不是依靠字数,或者作者使用了哪些修辞手法之类的看似完全客观但实际没有什么意义的评价手段。

但代码和小说还有些不一样,它实际存在两个读者:计算机和程序员。就像上篇文章里说的,即使所有程序员都看不懂这段代码,它也是可以被计算机理解并运行的。

所以对于代码质量的定义我需要于从两个维度分析:主观的,被人类理解的部分;还有客观的,在计算机里运行的状况。

既然存在主观部分,那么就会存在个体差异,对于同一段代码评价会因为看代码的人的水平不同而得出不一样的结论,这也是大多数新人面对的问题:他们没有一个可以执行的评价标准,所以写出来的代码质量也很难提高。

有些介绍代码质量的文章讲述的都是倾向或者原则,虽然说的很对,但是实际指导作用不大。所以在这篇文章里我希望尽可能把评价代码的标准用(我自认为)与实际水平无关的评价方式表示出来。

2.2.可读的代码

在权衡很久之后,我决定把可读性的优先级排在前面:一个程序员更希望接手一个有bug但是看的懂的工程,还是一个没bug但是看不懂的工程?如果是后者,可以直接关掉这个网页,去做些对你来说更有意义的事情。

2.2.1.逐字翻译

在很多跟代码质量有关的书里都强调了一个观点:程序首先是给人看的,其次才是能被机器执行,我也比较认同这个观点。在评价一段代码能不能让人看懂的时候,我习惯让作者把这段代码逐字翻译成中文,试着组成句子,之后把中文句子读给另一个人没有看过这段代码的人听,如果另一个人能听懂,那么这段代码的可读性基本就合格了。

用这种判断方式的原因很简单:其他人在理解一段代码的时候就是这么做的。阅读代码的人会一个词一个词的阅读,推断这句话的意思,如果仅靠句子无法理解,那么就需要联系上下文理解这句代码,如果简单的联系上下文也理解不了,可能还要掌握更多其它部分的细节来帮助推断。大部分情况下,理解一句代码在做什么需要联系的上下文越多,意味着代码的质量越差。

逐字翻译的好处是能让作者能轻易的发现那些只有自己知道的、没有体现在代码里的假设和可读性陷阱。无法从字面意义上翻译出原本意思的代码大多都是烂代码,比如“ms代表messageService“,或者“ms.proc()是发消息“,或者“tmp代表当前的文件”。

2.2.2.遵循约定

约定包括代码和文档如何组织,注释如何编写,编码风格的约定等等,这对于代码未来的维护很重要。对于遵循何种约定没有一个强制的标准,不过我更倾向于遵守更多人的约定。

与开源项目保持风格一致一般来说比较靠谱,其次也可以遵守公司内部的编码风格。但是如果公司内部的编码风格和当前开源项目的风格冲突比较严重,往往代表着这个公司的技术倾向于封闭,或者已经有些跟不上节奏了。

但是无论如何,遵守一个约定总比自己创造出一些规则要好很多,这降低了理解、沟通和维护的成本。如果一个项目自己创造出了一些奇怪的规则,可能意味着作者看过的代码不够多。

一个工程是否遵循了约定往往需要代码阅读者有一定经验,或者需要借助checkstyle这样的静态检查工具。如果感觉无处下手,那么大部分情况下跟着google做应该不会有什么大问题:可以参考 google code style ,其中一部分有对应的 中文版

另外,没有必要纠结于遵循了约定到底有什么收益,就好像走路是靠左好还是靠右好一样,即使得出了结论也没有什么意义,大部分约定只要遵守就可以了。

2.2.3.文档和注释

文档和注释是程序很重要的部分,他们是理解一个工程或项目的途径之一。两者在某些场景下定位会有些重合或者交叉(比如javadoc实际可以算是文档)。

对于文档的标准很简单,能找到、能读懂就可以了,一般来说我比较关心这几类文档:

  1. 对于项目的介绍,包括项目功能、作者、目录结构等,读者应该能3分钟内大致理解这个工程是做什么的。

  2. 针对新人的QuickStart,读者按照文档说明应该能在1小时内完成代码构建和简单使用。

  3. 针对使用者的详细说明文档,比如接口定义、参数含义、设计等,读者能通过文档了解这些功能(或接口)的使用方法。

有一部分注释实际是文档,比如之前提到的javadoc。这样能把源码和注释放在一起,对于读者更清晰,也能简化不少文档的维护的工作。

还有一类注释并不作为文档的一部分,比如函数内部的注释,这类注释的职责是说明一些代码本身无法表达的作者在编码时的思考,比如“为什么这里没有做XXX”,或者“这里要注意XXX问题”。

一般来说我首先会关心注释的数量:函数内部注释的数量应该不会有很多,也不会完全没有,个人的经验值是滚动几屏幕看到一两处左右比较正常。过多的话可能意味着代码本身的可读性有问题,而如果一点都没有可能意味着有些隐藏的逻辑没有说明,需要考虑适当的增加一点注释了。

其次也需要考虑注释的质量:在代码可读性合格的基础上,注释应该提供比代码更多的信息。文档和注释并不是越多越好,它们可能会导致维护成本增加。关于这部分的讨论可以参考简洁部分的内容。

2.2.4.推荐阅读

《代码整洁之道》

2.3.可发布的代码

新人的代码有一个比较典型的特征,由于缺少维护项目的经验,写的代码总会有很多考虑不到的地方。比如说测试的时候似乎没什么异常,项目发布之后才发现有很多意料之外的状况;而出了问题之后不知道从哪下手排查,或者仅能让系统处于一个并不稳定的状态,依靠一些巧合勉强运行。







请到「今天看啥」查看全文