您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

六个问题,教你如何写出干净优雅的代码

时间:2020-07-31 10:42:24  来源:  作者:

导读:为赶项目进度欠下一堆技术债怎么办?业务逻辑复杂,如何处理比较好?相似的功能要不要copy修改一下复用?怎么写代码注释?好代码无论对个人还是团队都至关重要,然而要写好代码却是一件非常不容易的事情,需要长期的经验积累和学习。关于写好代码,本文作者分享了6个入门的比较重要的点,希望对同学们有所启发。

写了多年的代码,始终觉得如何写出干净优雅的代码并不是一件容易的事情。按10000小时刻意训练的定理,假设每天8小时,一个月20天,一年12个月,大概也需要5年左右的时间成为大师。其实我们每天的工作中真正用于写代码的时间不可能有8个小时,并且很多时候是在完成任务,在业务压力很大的时候,可能想要达到的目标是如何尽快的使得功能work起来,代码是否干净优雅非常可能没有能放在第一优先级上,而是怎么快怎么来。

在这样的情况下是非常容易欠下技术债的,时间长了,这样的代码基本上无法维护,只能推倒重来,这个成本是非常高的。欠债要还,只是迟早的问题,并且等到要还的时候还要赔上额外的不菲的利息。还债的有可能是自己,也有可能是后来的继任者,但都是团队在还债。所以从团队的角度来看,写好代码是一件非常有必要的事情。如何写出干净优雅的代码是个很困难的课题,我没有找到万能的solution,更多的是一些trade off,可以稍微讨论一下。

代码是写给人看的还是写给机器看的?

在大部分的情况下我会认为代码是写给人看的。虽然代码最后的执行者是机器,但是实际上代码更多的时候是给人看的。我们来看看一段代码的生命周期:开发 --> 单元测试 --> Code Review --> 功能测试 --> 性能测试 --> 上线 --> 运维、Bug修复 --> 测试上线 --> 退休下线。开发到上线的时间也许是几周或者几个月,但是线上运维、bug修复的周期可以是几年。

六个问题,教你写出干净优雅的代码

 

在这几年的时间里面,几乎不可能还是原来的作者在维护了。继任者如何能理解之前的代码逻辑是极其关键的,如果不能维护,只能自己重新做一套。所以在项目中我们经常能见到的情况就是,看到了前任的代码,都觉得这是什么垃圾,写的乱七八糟,还是我自己重写一遍吧。就算是在开发的过程中,需要别人来Code Review,如果他们都看不懂这个代码,怎么来做Review呢。还有你也不希望在休假的时候,因为其他人看不懂你的代码,只好打电话求助你。这个我印象极其深刻,记得我在工作不久的时候,一次回到了老家休假中,突然同事打电话来了,出现了一个问题,问我该如何解决,当时电话还要收漫游费的,非常贵,但是我还不得不支持直到耗光我的电话费。

所以代码主要还是写给人看的,是我们的交流的途径。那些非常好的开源的项目虽然有文档,但是更多的我们其实还是看他的源码,如果开源项目里面的代码写的很难读,这个项目也基本上不会火。因为代码是我们开发人员交流的基本途径,甚至可能口头讨论不清楚的事情,我们可以通过代码来说清楚。代码的可读性我觉得是第一位的。各个公司估计都有自己的代码规范,遵循相关的规范保持代码风格的统一是第一步,推荐Google Style Guides | styleguideFramework Design Guidelines | Microsoft Docs。规范里一般都包括了如何进行变量、类、函数的命名,函数要尽量短并且保持原子性,不要做多件事情,类的基本设计的原则等等。另外一个建议是可以多参考学习一下开源项目中的代码。

KISS (Keep it simple and stupid)

一般大脑工作记忆的容量就是5-9个,如果事情过多或者过于复杂,对于大部分人来说是无法直接理解和处理的。通常我们需要一些辅助手段来处理复杂的问题,比如做笔记、画图,有点类似于在内存不够用的情况下我们借用了外存。

学CS的同学都知道,外存的访问速度肯定不如内存访问速度。另外一般来说在逻辑复杂的情况下出错的可能要远大于在简单的情况下,在复杂的情况下,代码的分支可能有很多,我们是否能够对每种情况都考虑到位,这些都有困难。为了使得代码更加可靠,并且容易理解,最好的办法还是保持代码的简单,在处理一个问题的时候尽量使用简单的逻辑,不要有过多的变量。

六个问题,教你写出干净优雅的代码

 

但是现实的问题并不会总是那么简单,那么如何来处理复杂的问题呢?与其借用外存,我更加倾向于对复杂的问题进行分层抽象。网络的通信是一个非常复杂的事情,中间使用的设备可以有无数种(手机,各种IOT设备,台式机,laptop,路由器,交换机...), OSI协议对各层做了抽象,每一层需要处理的情况就都大大地简化了。通过对复杂问题的分解、抽象,那么我们在每个层次上要解决处理的问题就简化了。其实也类似于算法中的divide-and-conquer, 复杂的问题,要先拆解掉变成小的问题,从而来简化解决的方法。

KISS还有另外一层含义,“如无必要,勿增实体” (奥卡姆剃刀原理)。CS中有一句 "All problems in computer science can be solved by another level of indirection", 为了系统的扩展性,支持将来的一些可能存在的变化,我们经常会引入一层间接层,或者增加中间的interface。在做这些决定的时候,我们要多考虑一下是否真的有必要。增加额外的一层给我们的好处就是易于扩展,但是同时也增加了复杂度,使得系统变得更加不可理解。对于代码来说,很可能是我这里调用了一个API,不知道实际的触发在哪里,对于理解和调试都可能增加困难。

KISS本身就是一个trade off,要把复杂的问题通过抽象和分拆来简单化,但是是否需要为了保留变化做更多的indirection的抽象,这些都是需要仔细考虑的。

DRY (Don't repeat yourself)

为了快速地实现一个功能,知道之前有类似的,把代码copy过来修改一下就用,可能是最快的方法。但是copy代码经常是很多问题和bug的根源。有一类问题就是copy过来的代码包含了一些其他的逻辑,可能并不是这部分需要的,所以可能有冗余甚至一些额外的风险。

另外一类问题就是在维护的时候,我们其实不知道修复了一个地方之后,还有多少其他的地方还需要修复。在我过去的项目中就出现过这样的问题,有个问题明明之前做了修复,过几天另外一个客户又提了类似的问题出现的另外的路径上。相同的逻辑要尽量只出现在一个地方,这样有问题的时候也就可以一次性地修复。这也是一种抽象,对于相同的逻辑,抽象到一个类或者一个函数中去,这样也有利于代码的可读性。

是否要写注释

个人的观点是大部分的代码尽量不要注释。代码本身就是一种交流语言,并且一般来说编程语言比我们日常使用的口语更加的精确。在保持代码逻辑简单的情况下,使用良好的命名规范,代码本身就很清晰并且可能读起来就已经是一篇良好的文章。特别是OO的语言的话,本身object(名词)加operation(一般用动词)就已经可以说明是在做什么了。重复一下把这个操作的名词放入注释并不会增加代码的可读性。并且在后续的维护中,会出现修改了代码,却并不修改注释的情况出现。在我做的很多Code Review中我都看到过这样的情况。尽量把代码写的可以理解,而不是通过注释来理解。

当然我并不是反对所有的注释,在公开的API上是需要注释的,应该列出API的前置和后置条件,解释该如何使用这个API,这样也可以用于自动产品API的文档。在一些特殊优化逻辑和负责算法的地方加上这些逻辑和算法的解释还是非常有必要的。

一次做对,不要相信以后会Refactoring

通常来说在代码中写上TODO,等着以后再来refactoring或者改进,基本上就不会再有以后了。我们可以去我们的代码库里面搜索一下TODO,看看有多少,并且有多少是多少年前的,我相信这个结果会让你很惊讶。

六个问题,教你写出干净优雅的代码

 

尽量一次就做对,不要相信以后还会回来把代码refactoring好。人都是有惰性的,一旦完成了当前的事情,move on之后再回来处理这些概率就非常小了,除非下次真的需要修改这些代码。如果说不会再回来,那么这个TODO也没有什么意义。如果真的需要,就不要留下这个问题。我见过有的人留下了一个TODO,throw了一个not implemented的exception,然后几天之后其他同学把这个代码带上线了,直接挂掉的情况。尽量不要TODO, 一次做好。

是否要写单元测试?

个人的观点是必须,除非你只是做prototype或者快速迭代扔掉的代码。

Unit tests are typically automated tests written and run by software developers to ensure that a section of an Application (known as the "unit") meets its design and behaves as intended. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method.

 

From Wikipedia

单元测试是为了保证我们写出的代码确实是我们想要表达的逻辑。当我们的代码被集成到大项目中的时候,之后的集成测试、功能测试甚至e2e的测试,都不可能覆盖到每一行的代码了。如果单元测试做的不够,其实就是在代码里面留下一些自己都不知道的黑洞,哪天调用方改了一些东西,走到了一个不常用的分支可能就挂掉了。我之前带的项目中就出现过类似的情况,代码已经上线几年了,有一次稍微改了一下调用方的参数,觉得是个小改动,但是上线就挂了,就是因为遇到了之前根本没有人测试过的分支。单元测试就是要保证我们自己写的代码是按照我们希望的逻辑实现的,需要尽量的做到比较高的覆盖,确保我们自己的代码里面没有留下什么黑洞。关于测试,我想单独开一篇讨论,所以就先简单聊到这里。

要写好代码确实是已经非常不容易的事情,需要考虑正确性、可读性、鲁棒性、可测试性、可以扩展性、可以移植性、性能。前面讨论的只是个人觉得比较重要的入门的一些点,想要写好代码需要经过刻意地考虑和练习才能真正达到目标!



Tags:代码   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
在SEO优化中,最重要的评估之一就是确定网站上存在哪些HTTP状态代码。这些代码可能会变得很复杂,成为一个难题,必须先解决这些难题,然后才能完成其他任务。例如,如果你放置的页面...【详细内容】
2021-12-24  Tags: 代码  点击:(4)  评论:(0)  加入收藏
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === 'a') { b = true} else { b = false}// goodb = a === 'a'2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Tags: 代码  点击:(5)  评论:(0)  加入收藏
前言本文提供将视频调整分辨率的Python代码,一如既往的实用主义。环境依赖ffmpeg环境安装,可以参考我的另一篇文章: windows ffmpeg安装部署_阿良的博客-CSDN博客ffmpy安装:pip...【详细内容】
2021-12-14  Tags: 代码  点击:(14)  评论:(0)  加入收藏
大家好, 我是林路,今天就给大家介绍Python代码都是用的什么编辑器写的?Jupyter Notebook ,没有Pycharm,没有Vscode,没有Sublime text。 只有一款工具:Jupyter Notebook 。工欲善其...【详细内容】
2021-12-09  Tags: 代码  点击:(26)  评论:(0)  加入收藏
在这篇文章中,我将与你分享一些关于JS的技巧,可以提高你的JS技能。1.避免if过长如果判断值满足多个条件,我们可能会这么写:if (value === 'a' || value === 'b'...【详细内容】
2021-11-17  Tags: 代码  点击:(22)  评论:(0)  加入收藏
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  Tags: 代码  点击:(37)  评论:(0)  加入收藏
《开源精选》是我们分享Github、Gitee等开源社区中优质项目的栏目,包括技术、学习、实用与各种有趣的内容。本期推荐的是一个由百度开源的低代码前端框架——amis...【详细内容】
2021-11-05  Tags: 代码  点击:(65)  评论:(0)  加入收藏
程序员是青春饭,这在国内似乎是公认的。所以很多公司不愿招大龄程序员,很多程序员也“知趣”地及早转型。有的做管理,有的做架构,我还见过改行卖保险的。总之,年龄大了不想敲代码...【详细内容】
2021-10-27  Tags: 代码  点击:(29)  评论:(0)  加入收藏
我们来看看我们拨号键盘除了能打电话还能干什么iphone 的拨号键盘除了用来拨号,其实暗藏代码输入星井06井可以查询手机真实的IMEI码,这个码是独一无二的没有双胞胎 输入星3001...【详细内容】
2021-10-25  Tags: 代码  点击:(78)  评论:(0)  加入收藏
1. 字符串有整型的相互转换String a = String.valueOf(2); //integer to numeric stringint i = Integer.parseInt(a); //numeric string to an int 2. 向文件末尾添加内容B...【详细内容】
2021-10-13  Tags: 代码  点击:(91)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条