乐趣区

关于代码规范:内功修炼好代码与坏代码

要写出好代码,首先须要晋升品位。

很多软件工程师写不好代码,在评审别人的代码时也看不出问题,就是因为不足对好代码规范的意识。

当初还有太多的软件工程师认为,代码只有能够正确执行就能够了。这是一种非常低的评估规范,很多重要的方面都被忽视了。

好代码的个性

好代码具备以下个性:

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)。

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

而后,咱们设想汽车外壳和外部变成了通明的,能够看到汽车外部的发动机、变速箱、传动杆、与刹车相干的外部安装等。而这些对驾驶者来说是不可见的,它们是对汽车的“零碎设计”。

最近我破费了几天的工夫,整顿了 1 份实践 + 实际的 Python 入门进阶教程,这或者是你见过十分好的一份学习材料之一。独家打造、完全免费,须要的同学能够关注 gzh【Python 编程学习圈】,发送“ 学习材料 ”获取~

退出移动版