干货 | 如何编写可读性更高的代码?

【转自公众号:CloudBest】
这些是我尝试编写简洁易读的代码时所考虑的事情。

优先考虑清晰度

有许多方法可以编写任何代码。有些会运行得更快,有些会占用更少的内存,有些会更易于测试。还有一些会更清楚。

编写清晰代码的第一步是使其成为优先事项。

这意味着您必须降低其他方面的优先级,例如速度。没有优先事项而不是其他优先事项(当所有事情都是优先事项时,什么都不是)是不存在的。

培养清晰感

写得好需要知道好的写作是什么样的,而创建清晰的代码则需要知道清晰的代码是什么样的。阅读备受赞誉的代码可以使您对好的外观有所了解。

对清晰代码的一种良好理解不会阻止您编写不可读的代码,但是它将告诉您哪些部分闻起来不正确。

编辑

关于如何编写代码的第一个想法很少会是最清楚的。

在完成写下第一个版本的脑力工作之后,通常更容易找到一种可读的方式来编写代码。重读您刚刚写的内容将有助于提出改进建议。

从解释开始

如果您不确定如何组织代码,请先说明要做什么,就像您在告诉其他人(或橡皮鸭)一样。写下:“好吧,如果删除了用户,或者订单已经在处理中,我们需要跳过它……”进行解释,然后将其转换为代码。
在布置代码时,最好是在人际交流方面进行思考,而不要在机器抽象方面进行思考。

注释

添加注释,以解释代码为何执行其正在执行的操作,或以其结构化方式进行结构化。

仅阅读逻辑并不能告诉您为什么作者认为这是正确的逻辑。您可能不知道某些业务原因-也许美国境外的用户有时将街道号码放在地址第一行的末尾。也许有一些技术细节-这个查询以这种怪异的方式构造,以说服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将“如何”移至单独的方法。它仅与“什么”有关,这意味着它停留在一个抽象层次上。

使每个函数处于一个抽象级别,并将较低级别的细节委派给较低抽象级别的方法。具有单一抽象级别的方法往往读起来就像是关于正在发生的事情的故事。

突破功能

通过将大型函数分解为较小的函数,可以使它们(有时!)更具可读性。
有时,该函数的作用类似于一系列步骤,在这种情况下,它可以很好地为每个步骤提取一个函数。在其他时候,有不同的决定要做出,每个决定都可以在不同的功能中做出。也许功能的某些部分像做出决定一样起作用,而某些部分像采取行动一样起作用。您可以使用许多不同的维度来分解功能。需要练习才能善于看到正确的使用方法。

较小的功能有一些优点:

逻辑的每一位都有一个名称。这使您更容易知道逻辑的每一位是做什么用的,并帮助您找到一些逻辑所在的位置。

范围内的变量较少。

当您查看堆栈跟踪或运行调试器时,很容易分辨出程序在想什么。

小型功能可以单独测试。

计算机可以正常工作,完全没有任何功能。为了程序员的存在而存在函数,因此请充分利用它们。

不要破坏功能

“不要重复自己的想法”(DRY)常常被认为太过分了。

现在,将魔术数字提取为常量并拥有一个逻辑副本以做出特定决策是一个非常好的主意。重复这些代码位是一个坏主意。

当碰巧共享少量行的两个功能成为重复数据删除的目标时,DRY开始走得太远。完全避免重复的行意味着您将得到混乱的,无意义的抽象,这些抽象仅用于容纳那几条共享行。这使得代码难以更改,因为两个不相关的代码片段的结构将被束缚在一起。

是否应该对某些代码进行重复数据删除的测试很简单:如果更改了一个代码而不更改另一个代码,会发生什么不好的事情?如果答案是肯定的,则为其提供唯一的真理来源。如果没有,请考虑不理会它。

DRY的目的不是在代码库上运行手动压缩过程,而是避免依赖关系,在该依赖关系中需要手动保持代码的两部分同步。记住,对代码进行重复数据删除与创建抽象不是一回事。

避免配置功能

优先选择许多功能,而不是一些可配置的功能。

我确定您已经看过这样的故事:您从一个在三个不同地方调用的干净函数开始。您想在第四位使用它,但是它需要做一些稍有不同的事情,因此您添加了一个配置参数。然后,第一个调用者获得一项新功能,需要另外两个配置参数。第五个用例添加了自己的特殊参数。呼叫者2的速度太慢,因此您添加了另一个参数来跳过部分工作。

不知何故,您一开始就做一件事情的干净函数现在有5个配置参数,并且可能执行2 ^ 5 = 32种不同的事情(或更多)!

最好有多个功能,每个功能只做一件事。

一旦有了单独的功能,当然就会重复。当这些共享部分需要保持同步时,请应用DRY并将其提取到共享功能中。如果功能已细分为决策和步骤的子功能,则这会更容易。

记住,几行重复就可以了!如果每个单独的函数在列表上都有自己的for循环,则这是非常可接受的重复。

这种方法的一个优点是,当一个用例消失时,您可以轻松删除相关功能。您无需深入研究复杂函数的逻辑即可弄清楚用于该特定选项集的部分。

专用功能的读者会发现,它更容易理解其功能。

(请注意,如果您控制该函数的所有调用者,那么这才是正确的方法。如果您的函数是公共API的一部分,则此处的理由并不适用,因为您不知道所有用例是什么或将是什么用例是。)

不要过早优化

赛车的行驶速度比普通赛车要快,但要牺牲硬座,发出很多噪音和缺乏空调的代价。如果您不知道自己的功能将需要成为赛车,请不要剥离空调。让生物安逸地工作-专注于编写易于阅读的代码,而不是易于计算机运行的代码。

过早的概括也是如此。如果您不需要拖运大量的东西,则不会购买自卸车,因此您也不应使您的代码能够满足可能永远不会发生的各种需求。

转译自:Jeremy
【转自公众号:CloudBest】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值