要写出好代码,首先需要提升品位。

很多软件工程师写不好代码,在评审他人的代码时也看不出问题,就是因为缺乏对好代码标准的认识。

现在还有太多的软件工程师认为,代码只要可以正确执行就可以了。这是一种非常低的评价标准,很多重要的方面都被忽视了。

好代码的特性

好代码具有以下特性。

1. 鲁棒(Solid and Robust)

代码不仅要被正确执行,我们还要考虑对各种错误情况的处理,比如各种系统调用和函数调用的异常情况,系统相关组件的异常和错误。

对很多产品级的程序来说,异常和错误处理的逻辑占了很大比例。

2. 高效(Fast)

程序的运行应使用尽量少的资源。资源不仅仅包括CPU,还可能包括存储、I/O等。

设计高效的程序,会运用到数据结构和算法方面的知识,同时要考虑到程序运行时的各种约束条件。

3. 简洁(Maintainable and Simple)

代码的逻辑要尽量简明易懂,代码要具有很好的可维护性。对于同样的目标,能够使用简单清楚的方法达成,就不要使用复杂晦涩的方法。

“大道至简”,能否把复杂的问题用简单的方式实现出来,这是一种编程水平的体现。

4. 简短(Small)

在某种意义上,代码的复杂度和维护成本是和代码的规模直接相关的。在实现同样功能的时候,要尽量将代码写得简短一些。

简洁高于简短。这里要注意,某些人为了能把代码写得简短,使用了一些晦涩难懂的描述方式,降低了代码的可读性。这种方式是不可取的。

5. 可测试(Testable)

代码的正确性要通过测试来保证,尤其是在敏捷的场景下,更需要依赖可自动回归执行的测试用例。

在代码的设计中,要考虑如何使代码可测、易测。一个比较好的实践是使用TDD(Test-Driven Development,测试驱动开发)的方法,这样在编写测试用例的时候会很快发现代码在可测试性方面的问题。

6. 共享(Re-Usable)

大量的程序实际上都使用了类似的框架或逻辑。由于目前开源代码的大量普及,很多功能并不需要重复开发,只进行引用和使用即可。

在一个组织内部,应鼓励共享和重用代码,这样可以有效降低代码研发的成本,并提升代码的质量。

实现代码的共享,不仅需要在意识方面提升,还需要具有相关的能力(如编写独立、高质量的代码库)及相关基础设施的支持(如代码搜索、代码引用机制)。

7. 可移植(Portable)

某些程序需要在多种操作系统下运行,在这种情况下,代码的可移植性成为一种必需的能力。

要让代码具有可移植性,需要对所运行的各种操作系统底层有充分的理解和统一抽象。一般会使用一个适配层来屏蔽操作系统底层的差异。

一些编程语言也提供了多操作系统的可移植性,如很多基于Python语言、Java语言、Go语言编写的程序,都可以跨平台运行。

8. 可观测(Observable) / 可监控(Monitorable)

面对目前大量存在的在线服务(Online Service)程序,需要具备对程序的运行状态进行细致而持续监控的能力。

这要求在程序设计时就提供相关的机制,包括程序状态的收集、保存和对外输出。

9. 可运维(Operational)

可运维已经成为软件研发活动的重要组成部分,可运维重点关注成本、效率和稳定性三个方面。

程序的可运维性和程序的设计、编写紧密相关,如果在程序设计阶段就没有考虑可运维性,那么程序运行的运维目标则难以达成。

10. 可扩展(Scalable and Extensible)

可扩展包含“容量可扩展”(Scalable)和“功能可扩展”(Extensible)两方面。

在互联网公司的系统设计中,“容量可扩展”是重要的设计目标之一。系统要尽量支持通过增加资源来实现容量的线性提高。

快速响应需求的变化,是互联网公司的另外一个重要挑战。可考虑使用插件式的程序设计方式,以容纳未来可能新增的功能,也可考虑使用类似Protocol Buffer 这样的工具,支持对协议新增字段。

以上十条标准,如果要记住,可能有些困难。我们可以把它们归纳为四个方面,见表1。

表1  对一流代码特性的汇总分类

坏代码的例子

关于好代码,上面介绍了一些特性,本节也给出坏代码(Bad Code)的几个例子。关于坏代码,本书没有做系统性总结,只是希望通过以下这些例子的展示让读者对坏代码有直观的感觉。

1. 不好的函数名称(Bad Function Name)

如do(),这样的函数名称没有多少信息量;又如myFunc(),这样的函数名称,个人色彩过于强烈,也没有足够的信息量。

2. 不好的变量名称(Bad Variable Name)

如a、b、c、i、j、k、temp,这样的变量名称在很多教科书中经常出现,很多人在上学期间写代码时也会经常这样用。如果作为局部变量,这样的名称有时是可以接受的;但如果作为作用域稍微大的变量,这样的名称就非常不可取了。

3. 没有注释(No Comments)

有写注释习惯的软件工程师很少,很多软件工程师认为写注释是浪费时间,是“额外”的工作。但是没有注释的代码,阅读的成本会比较高。

4. 函数不是单一目的(The Function has No Single Purpose)

如LoadFromFileAndCalculate()。这个例子是我编造的,但现实中这样的函数其实不少。很多函数在首次写出来的时候,就很难表述清楚其用途;还有一些函数随着功能的扩展,变得越来越庞杂,也就慢慢地说不清它的目的了。

这方面的问题可能很多人都没有充分地认识到——非单一目的的函数难以维护,也难以复用。

5. 不好的排版(Bad Layout)

不少人认为,程序可以正常执行就行了,所以一些软件工程师不重视对代码的排版,认为这仅仅是一种“形式”。

没有排好版的程序,在阅读效率方面会带来严重问题。这里举一个极端的例子:对于C语言来说,“;”可作为语句的分割符,而“缩进”和“换行”对于编译器来说是无用的,所以完全可以把一段C语言程序都“压缩”在一行内。这样的程序是可以运行的,但是对人来说,可读性非常差。这样的程序肯定是我们非常不希望看到的。

6. 无法测试(None Testable)

程序的正确性要依赖测试来保证(虽然测试并不能保证程序完全无错)。无法或不好为之编写测试用例的程序,是很难有质量保证的。

好代码从哪里来

上一节说明了好代码的特性,本节来分析好代码是如何产出的。

  好代码不止于编码

好代码从哪里来?

对于这个问题,很多读者肯定会说:“好代码肯定是写出来的呀。”

我曾做过多次调研,发现很多软件工程师日常所读的书确实是和“写代码”紧密相关的。

但是,这里要告诉读者的是,代码不只是“写”出来的。在很多年前,我所读的软件工程方面的教科书就告诉我,编码的时间一般只占一个项目所花时间的 10%。我曾说过一句比较有趣的话:

“如果一个从业者告诉你,他的大部分时间都在写代码,那么他大概率不是一个高级软件工程师。”

那么,软件工程师的时间都花到哪里去了呢?软件工程师的时间应该花在哪里呢?

好的代码是多个工作环节的综合结果。

(1)在编码前,需要做好需求分析和系统设计。而这两项工作是经常被大量软件工程师忽略或轻视的环节。

(2)在编码时,需要编写代码和编写单元测试。对于“编写代码”,读者都了解;而对于“编写单元测试”,有些软件工程师就不认同了,甚至还有人误以为单元测试是由测试工程师来编写的。

(3)在编码后,要做集成测试、上线,以及持续运营/迭代改进。这几件事情都是要花费不少精力的,比如上线,不仅仅要做程序部署,而且要考虑程序是如何被监控的。有时,为了一段程序的上线,设计和实施监控的方案要花费好几天才能完成。

因此,一个好的系统或产品是以上这些环节持续循环执行的结果。

  需求分析和系统设计

1. 几种常见的错误现象

相对于编码工作,需求分析和系统设计是两个经常被忽视的环节。在现实工作中,我们经常会看到以下这些现象。

(1)很多人错误地认为,写代码才是最重要的事情。不少软件工程师如果一天没有写出几行代码,就会认为工作没有进展;很多管理者也会以代码的产出量作为衡量工作结果的主要标准,催促软件工程师尽早开始写代码。

(2)有太多的从业者,在没有搞清楚项目目标之前就已经开始编码了。在很多时候,项目目标都是通过并不准确的口头沟通来确定的。例如:

“需要做什么?”

“就按照×××网站的做一个吧。”

(3)有太多的从业者,在代码编写基本完成后,才发现设计思路是有问题的。他们在很多项目上花费很少(甚至没有花费)时间进行系统设计,对于在设计中所隐藏的问题并没有仔细思考和求证。基于这样的设计投入和设计质量,项目出现设计失误也是很难避免的。而面对一个已经完成了基本编码的项目,如果要“动大手术”来修改它,相信每个有过类似经历的人都一定深知那种感受——越改越乱,越改越着急。

以上这几种情况,很多读者是不是都有过类似经历?

2. 研发前期多投入,收益更大

关于软件研发,首先我们需要建立一个非常重要的观念。

在研发前期(需求分析和系统设计)多投入资源,相对于把资源都投入在研发后期(编码、测试等),其收益更大。

这是为什么呢?

要回答这个问题,需要从软件研发全生命周期的角度来考量软件研发的成本。除编码外,软件测试、上线、调试等都需要很高成本。如果我们把需求搞错了,那么与错误需求有关的设计、编码、测试、上线等成本就都浪费了;如果我们把设计搞错了,那么与错误设计相关的编码、测试、上线的成本也就浪费了。

如果仔细考量那些低效的项目,会发现有非常多的类似于上面提到的“浪费”的地方。软件工程师似乎都很忙,但是在错误方向上所做的所有努力并不会产生任何价值,而大部分的加班实际上是在做错误的事情,或者是为了补救错误而努力。在这种情况下,将更多的资源和注意力向研发前期倾斜会立刻收到良好的效果。

3. 修改代码和修改文档,哪个成本更高

很多软件工程师不愿意做需求分析和系统设计,是因为对“写文档”有着根深蒂固的偏见。这里问大家一个问题,如果大家对这个问题能给出正确的回答,那么在“写文档”的意识方面,一定会有很大的转变。

任何人都不是神仙,无法一次就把所有事情做对。对于一段程序来说,它一定要经过一定周期的修改和迭代。这时有两种选择:

选择一:修改文档。在设计文档时完成迭代调整,待没有大问题后再开始编码。

选择二:修改代码。只有粗略的设计文档,或者没有设计文档,直接开始编码,所有的迭代调整都在代码上完成。

请大家判断,修改代码和修改文档,哪个成本更高?

在之前的一些分享交流会上,对于这个问题,有人会说,修改文档的成本更高。因为在修改文档后还要修改代码,多了一道手续。而直接修改代码,只需要做一次,这样更直接。

这个回答说明了回答者没有充分理解“先写文档,后写代码”的设计方法。如果没有充分重视设计文档的工作,在输出的设计文档质量不高的情况下就开始编码,确实会出现以上提到的问题。但是,如果在设计文档阶段就已经做了充分考虑,会减少对代码的迭代和反复。

对于同样的设计修改,“修改代码”的成本远高于“修改文档”。这是因为,在设计文档中只会涉及主要的逻辑,那些细小的、显而易见的逻辑不会在设计文档中出现。在修改设计文档时,也只会影响到这些主要逻辑。而如果在代码中做修改,不仅会涉及这些主要逻辑,而且会涉及那些在文档中不会出现的细小逻辑。对于一段程序来说,任何一个逻辑出现问题,程序都是无法正常运行的。

4. 需求分析和系统设计之间的差别

很多读者无法清楚地区分“需求分析”和“系统设计”之间的差别,于是会发现,在写出的文档中,有些需求分析文档里出现了系统设计的内容,而有些系统设计文档里又混杂了需求分析的内容。

我们用几句话可以非常明确地给出二者的差异。

(1)需求分析:定义系统/软件的黑盒的行为,它是从外部(External)看到的,在说明“是什么”(What)。

(2)系统设计:设计系统/软件的白盒的机制,它是从内部(Internal)看到的,要说明“怎么做”(How)和“为什么”(Why)。

比如,对一辆汽车来说,首先使用者从外部可以看到车厢、车轮,坐在车里可以看到方向盘、刹车踏板、油门踏板等;操作方向盘可以改变汽车的行驶方向,脚踩刹车踏板、油门踏板可用于减速和加速。以上这些是对汽车的“需求分析”。

然后,我们想象汽车外壳和内部变成了透明的,可以看到汽车内部的发动机、变速箱、传动杆、与刹车相关的内部装置等。而这些对驾驶者来说是不可见的,它们是对汽车的“系统设计”。

本文节选自《代码的艺术:用工程思维驱动软件开发》一书,想要了解更多相关内容,欢迎阅读本书!

抽奖赠书

截止时间:2022年3月26日 12:00

如何抽奖:点击下方卡片,关注并回复关键词 :20220322

下次你更希望我们送哪本书呢?

留言告诉我们!

代码要想写得好,品位必须提一提!相关推荐

  1. dump java崩溃自动 不生成_GitHub 自动生成 .gitignore,只想写代码不重要的事情别烦我...

    代码上传 GitHub 为何半途而废?因为只想写代码,readme 不想写,gitignore 不想管. readme 不想写已经有救了,我们发现了 readme 自动生成工具 你已经是个成熟的 Gi ...

  2. 为什么项目经理依然想写代码?

    [CSDN 编者按]从工程师或技术人员过渡到经理的最大困难之一就是认识到生产代码不再是您的主要目标,然而这么久以来"经理应不应该继续写代码"成为了人们口中经常谈到的话题,对此你又是 ...

  3. AI之Tool:GitHub Copilot(一款人工智能编程小助手—猜你想写的代码)的简介、安装、使用方法之详细攻略

    AI之Tool:GitHub Copilot(一款人工智能编程小助手-猜你想写的代码)的简介.安装.使用方法之详细攻略 目录 Copilot人工智能工具的简介 1.GitHub Copilot的安全性 ...

  4. OSChina 周四乱弹 —— 不想写代码,想划水

    2019独角兽企业重金招聘Python工程师标准>>> Osc乱弹歌单(2018)请戳(这里) [今日歌曲] @胖达panda :分享Jem的单曲<Yellow>: &l ...

  5. 【观点】想写代码?离开你的电脑吧!

    关键词:代码 | 作者:丘建红 | 收藏这篇资讯 导读:本文由<Want to write some code? Get away from your computer!>编译而来,译文来 ...

  6. 非著名架构师告诉你,代码该如何写,才能自己写的容易别人看的也不痛苦

    来自:编程新说 切身感受 在这个世界上,最难看懂的文档,永远是同事写的需求文档.最难看懂的代码,永远是同事写的业务代码. 我很纳闷,像Spring这样的官方英文文档,我看起来也不太费劲,但是需求文档, ...

  7. python游戏最简单代码-用python写游戏脚本原来这么简单

    前言 最近在玩儿公主连结,之前也玩儿过阴阳师这样的游戏,这样的游戏都会有个初始号这样的东西,或者说是可以肝的东西. 当然,作为一名程序员,肝这种东西完全可以用写代码的方式帮我们自动完成.游戏脚本其实并 ...

  8. Leetcode惊现马化腾每天刷题?为啥大佬都这么努力?代码你打算写到几岁?

    前几天,一张未证真伪的截图流传,图中显示马化腾几乎每天都会在 Leetcode 上提交代码. 截图还贴出一个 Leetcode 账户地址.该地址的头像已从马化腾的照片换成腾讯 logo,昵称也从&qu ...

  9. 如果你想写自己的Benchmark框架

    文章目录 简介 八条军规 第一条军规 第二条军规 第三条军规 第四条军规 第五条军规 第六条军规 第七条军规 最后一条军规 总结 简介 使用过JMH的同学一定会惊叹它的神奇.JMH作为一个优秀的Ben ...

最新文章

  1. 【matlab】第三章数组和数组的运算
  2. 详解深度学习中的Normalization,不只是BN(1)
  3. 从封装函数到实现简易版自用jQuery (一)
  4. [转]TCP(HTTP)长连接和短连接区别和怎样维护长连接
  5. 计算机考研英语什么时候考试,计算机考研:早期复习三大注意事项
  6. Linux shell中比较操作符“==”与“-eq”对比
  7. 下载debian老版本的链接
  8. 从单体迈向Serverless的避坑指南
  9. 聚类算法论文研读 record
  10. 热传导方程有限差分法实现matlab,热传导方程有限差分法的MATLAB实现-史策
  11. 关于一个自制练字脚本
  12. 查找恶意的TOR中继节点
  13. 【已解决】TF_REPEATED_DATA ignoring data with redundant timestamp for frame
  14. Python 如何画出漂亮的地图?
  15. python扫雷 高级算法_利用Python实现自动扫雷
  16. linux命令查询网站
  17. 宝利通group终端升级指导书
  18. matlab计算膜孔径分布,应用Matlab绘制活性炭吸附等温线和孔径分布图
  19. dB、dBm、dBw的区别
  20. 实现一个基于jQuery的图片轮播效果(带自动播放)

热门文章

  1. string用法总结
  2. 从注册流程 分析如何安全退出多个Activity 多种方式(附DEMO)
  3. 企业 SOA 设计(1)–ESB 设计
  4. Hlink的Analysis基本搞定了
  5. Mysql 导入导出csv 中文乱码问题的解决方法
  6. linux openssl libcurl库 交叉编译
  7. linux c 自定义信号 测试kill信号发送
  8. golang goland报错错误 $GOPATH/go.mod exists but should not 解决方法
  9. linux pssh parallel-ssh 批量执行远程shell命令
  10. linux sed命令 删除指定行