AI 年度盘点与2025发展趋势展望,50+案例解析亮相AICon 了解详情
写点什么

怎样写出可读性高的代码?

  • 2021-03-29
  • 本文字数:2672 字

    阅读完需:约 9 分钟

怎样写出可读性高的代码?

清楚你的优先级

代码的写法有很多种:有的运行起来很快,有的只会占用少量内存,有的更容易测试,而有的代码则有很高的可读性。


若要编写思路清晰的代码,第一步就是要将可读性放在第一位。


这也意味着势必要降低其他因素的优先级。如果把所有因素都作为最高优先级,就意味着没有优先级。

培养清晰的意识

想要写出好代码,首先要知道什么才是好代码,想要写出思路清晰的代码,也要了解什么才是思路清晰。多阅读一些质量上乘的代码可以让我们对好代码有个大概的认知。


了解什么才是优秀代码并不能杜绝我们继续写出糟糕的代码,但至少能让我们知道代码的哪里不对劲。

修订

编写代码时,我们最初所想的思路未必清晰。在大多数情况下,只有在第一次完成代码后,我们才能找到更适合的思路。反复阅读已完成的代码才会带来更改的空间。

从解释开始

如果我们还搞不清代码结构,那么可以试着想象一下怎样向他人解释清楚或者把逻辑思路写下来,比如“如果删除账户,那么我们需要跳过 xxx。如果 xxx 的进程还没有结束,那么……”。然后把这套逻辑翻译成代码就很顺了。


写程序时,带入人类沟通方式而不是计算机中的抽象概念要更容易。

注释

代码中的注释可以解释某段代码的用处,或者是程序结构为什么要这么写。


单单是阅读程序并不会告诉我们作者所想就是正确的逻辑。里面可能会有我们不了解的商业规则:美国境外的用户有时会把街道名写到地址栏第一行的最末尾。里面也可能有一些技术小技巧:以某种奇怪的方式构造查询,从而让 Postgres 正确地优化它。诸如此类的代码细节,都是只有了解逻辑背后的背景情况下才能彻底明白为什么要这么写的。


代码不会说话。如果我们决定跳过某些步骤,但又懒得留下注释解释为什么,过两天再回来看这段代码恐怕就真没人知道你当时在想什么了。


部分代码可能读两遍就能想明白个中缘由,但为了保险起见,还是不要给自己的大脑添加不必要的负担。

不要搞混层次

不要搞混函数中的抽象层次。


这段“欢迎”代码层次混乱:


def welcome(self):  results = db.query(    'SELECT EXISTS 1 FROM emails WHERE kind = ? AND user = ?',    'welcome_email', self.user.id,  )  if results[0]:    return  self.send_welcome_email()
复制代码


这段则是相对整齐的:


def welcome(self):  if not self.has_sent_welcome_email():    self.send_welcome_email()
复制代码


函数中混乱的抽象层次会让读者思考代码用途和实现方式时被迫进行思维跳跃。当前抽象层次的代码告诉我们代码在做什么,而下一层次的代码则是关于代码要如何实现的。


在例子里的“welcome”函数中,我们首先在数据库中查询是否有过往邮件记录,如果没有则发送一封欢迎邮件。请注意,第二个版本中的“welcome”函数将查询部分放到了另一个函数中,“welcome”中仅仅关注“做什么”,这就是将函数中的抽象层次保持在了同一层,逻辑也更加清晰。不同函数分散在不同抽象层次,将较低层次的实现细节委托给较低抽象层次的函数。

分解函数

有时,分解大体积函数到子函数会更便于阅读。


对于分步骤执行的函数,将函数中的每个步骤都分解成子函数效果会更好。而对于其他如决策类的函数,不同的决策会引向不同的函数:有的部分负责制定决策,有的则是负责执行决策。分解函数的方法有很多种维度,只有通过不断的练习才能一眼看穿哪种才是正确的。


小体积函数有以下几点好处:


  • 每一部分的逻辑都有自己函数名。知道每一块逻辑负责什么更方便我们找到这些函数应当被放在哪

  • 作用域中变量更少

  • 在运行堆栈轨迹和调试时能更清晰地看出函数的作用

  • 小型函数可以被单独测试


其实,没有任何函数计算机也能运行得好好的,函数的存在只是为了服务于程序员,所以还请多多利用它们。

不要分解函数

不要重复你自己(don't repeat yourself, DRY)的意思经常被过度解读。


如今,抽取魔法数常量,以及针对某类特定决策的逻辑副本,已经算是公认的标准答案。此类重复的代码的确不好。而 DRY 的过度解读是指面对区区两行的重复代码,便如临大敌恨不得除之而后快。完全避免任何的重复代码意味着我们最后将面对一堆毫无意义、令人迷惑的代码,其存在只为了防止程序中的两三行重复代码。再加上由于在逻辑上毫不相干的两段代码被迫捆绑在一起,代码也更加难以修改。


判断一段代码的重复是否可容忍很简单:修改 A 段代码,保留 B 段不变,如果程序报错,那么就把 A 和 B 整理到同一段代码;如果无事发生,那么就放着别管。DRY 并不代表我们需要手动压缩代码库,而是为了避免两段代码要依赖于手动的同步。请记住,重复代码和抽象创造并不是同一件事。

避免使用可配置函数

宁可要十个零参数的小函数,也不要一个带十个参数的函数。


诸位对类似的事一定不陌生:初始干净的函数,只在三个不同的地方被调用。而当我们想要在第四处调用时,我们需要做一点小的调整,添加一个参数。但这样第一个 caller 就多了一个新功能,也需要多添加两个可配置的参数。等到第五个用例,我们还要再为它添加独特的参数,以此类推。但反过来我们就又会发现第二个 caller 跑起来太慢了,所以只好再添加另一个参数来跳过部分繁琐的程序。


不知不觉中,我们那个干净整洁的、只负责一件事的函数现在有了五个配置参数,现在能做的事情甚至可以达到 2 的五次方种!


这种情况下,将这一整个复杂的函数拆分成子函数,每个函数只负责各自的事就会好上很多。


但这样以来,又不可避免会出现重复。当这些重复的部分需要保持同步时,我们可以利用 DRY 的思路,将相同的部分抽取到子函数中。这时,做决策和考虑步骤就会容易很多。


请记住,区区几行重复代码是没问题的!像是在不同 list 上跑 for 循环的代码,这类就是可以接受的重复。


这种方法的好处之一是当其中一个用例被删除时,你可以轻松删除掉对应的函数,而不是在复杂函数的逻辑里掘地三尺试图找到对应的选项。只关注某个特定函数的读者也会更容易理解它们的用处。


(注意,当你能负责所有的 caller 时,这种方法才是正确的。如果你的函数只是公共 API 的一部分,那么请不要考虑使用这种方法。因为你并不清楚所有的用例都是什么,也不知道未来会有什么样的用例)

不要过早地进行优化

竞速赛车跑得比普通轿车要快,这点毋庸置疑。但这也是赛车在牺牲了柔软座椅、低噪音,以及车载空调的条件下。如果我们的程序不需要做竞速赛车,那就不要过早地拆掉空调。逐渐熟悉程序的构造,先从编写易于人理解的代码开始,不要一上来就试图挑战计算机的运行速度。


同理,也不应过早开始泛化。没人会在不需要处理大量物品的时候就买入一辆自卸货车,在没有过多需求的时候,我们也不用提前编写多余功能的代码。


原文链接:


[http://jeremymikkola.com/posts/2021_02_02_how_to_write_readable_code.html](

2021-03-29 13:431801
用户头像

发布了 151 篇内容, 共 91.7 次阅读, 收获喜欢 198 次。

关注

评论

发布
暂无评论
发现更多内容

开心档-软件开发入门之MongoDB 创建集合

雪奈椰子

mongodb 开心档

如何让OpenHarmony编译速度“狂飙”

离北况归

OpenHarmony

NFTScan 正式上线 Fantom 网络 NFTScan 浏览器和 NFT API 数据服务

NFT Research

NFT 数据基础设施

LeaRun快速开发平台:自由搭建个性化门户

力软低代码开发平台

新年新气象,老兵开新坑

致知Fighting

Java Go 服务器

sun4.0泰山众筹模式项目系统开发技术讲解放哪(Demo)

I8O28578624

如何通过jstat命令进行查看堆内存使用情况

华为云开发者联盟

后端 开发 华为云 企业号 2 月 PK 榜 华为云开发者联盟

理论+实践,教你如何使用Nginx实现限流

华为云开发者联盟

后端 开发 华为云 企业号 2 月 PK 榜 华为云开发者联盟

10大知识管理软件厂商有对比

爱吃小舅的鱼

项目管理 知识管理软件

简单概述Serverless

天翼云开发者社区

如何用Apipost校验响应结果

爱研究代码的极客人

APi设计 JSON Schema apipost

线上网络丢包引起的接口响应时间过慢,快速排查案例

KINDLING

Java 运维 网络 丢包 eBPF&Linux

SparK 用稀疏掩码为卷积设计 Bert 预训练

Zilliz

计算机视觉

工业生产环境下,时序数据库 TDengine 如何打造全面有效的数字化监控?

TDengine

数据库 tdengine 时序数据库

单线程架构的Redis如此之快的 4 个原因

JAVA旭阳

redis 缓存

使用Jira盗版会存在的6大风险

爱吃小舅的鱼

项目管理 软件开发 软件管理

Studio One2023永久和谐版水果编曲工具使用教程

茶色酒

Studio One 5 Studio One2023

带你读论文丨S&P21 Survivalism: Living-Off-The-Land 经典离地攻击

华为云开发者联盟

人工智能 华为云 论文 企业号 2 月 PK 榜 华为云开发者联盟

开心档-软件开发入门之MongoDB 覆盖索引查询

雪奈椰子

mongodb Ppython 操作mongodb库 开心档

CompletableFuture实现异步转同步

FunTester

9种跨域方式实现原理

华为云开发者联盟

开发 华为云 企业号 2 月 PK 榜 华为云开发者联盟

MySQL中的distinct和group by哪个效率更高?

Steven

R2M分布式锁原理及实践

京东科技开发者

redis 框架解析 企业号 2 月 PK 榜 r2m 分布式锁原理

OpenHarmony标准系统内核学习【2】CPU轻量级隔离特性

离北况归

OpenHarmony

聊聊Docker镜像

天翼云开发者社区

Docker 镜像

行云洞见|为何行业权威都预测“云原生IDE 将成为常态”?

行云创新

ide 云原生 云端IDE Cloud IDE TitanIDE

APISIX Ingress 如何使用 Cert Manager 管理证书

API7.ai 技术团队

证书 api 网关 APISIX Ingress Controller

实战分享 | 金融数据采集报送平台实践

葡萄城技术团队

基于GIS+WebGL智慧消防3D可视化云控系统

2D3D前端可视化开发

智慧消防 消防物联网云平台 消防三维可视化 智慧消防系统 消防云控平台

谈谈我工作中的23个设计模式

阿里巴巴中间件

阿里云 云原生

单线程 Redis 如此之快的 4 个原因

C++后台开发

redis 中间件 后端开发 单线程 C++开发

怎样写出可读性高的代码?_语言 & 开发_Jeremy Mikkola_InfoQ精选文章