首页 > 其他分享 >编写可读性代码的艺术

编写可读性代码的艺术

时间:2024-07-02 12:30:33浏览次数:3  
标签:function const 可读性 代码 storage value return 编写

编写可读性代码的艺术

最近阅读了《编写可读代码的艺术》一书,感觉很有收获,现在结合自己的理解再来总结编写可读性代码的技巧,会用 js 举例,并且针对日常开发中常见的代码异味给出我的改进建议。

学会该书的大部分技巧并付诸实践,无法保证你立马写出完美的代码,但能保证你写出可读性不是非常糟糕的代码,保证你的码德下限。

可读性 = 可测试性 = 设计良好 = 可维护 = 代码质量 = …,衡量代码的各种指标,都是正相关的,开发程序的大部分时间是在阅读代码(自己的和他人的),所以保证了可读性,其他指标也不会差。

衡量代码的可读性

大部分程序员,全靠自觉、灵感和经验编写代码,往往很难一步到位写出可读性高的代码。

我看过一些前端组长(猪长)、前端架构师(加狗屎)写的代码,简直惨不忍睹,让人有骂娘的冲动。

比如这种:

13个参数
13个参数,无法轻易调用。

不可读的代码往往都会有这样或那样的问题。

软件的成本由开发成本和维护成本组成,而往往维护成本要远高于开发成本,维护成本主要花在理解代码和修改代码上,可读性高、设计良好的代码可大大降理解和修改代码的成本。

可见代码的可读性至关重要。

如何衡量代码的可读性呢?

代码可读性和代码被他人理解的时间成反比,即他人理解代码的时间越少,可读性越高。

如何定义他人?根据我的经验,高年级本科生或者研究生或者工作 2 年内的程序员,又或者,你的一个普通同事。

同事是和你协作的人,让和你协作的人能快速地理解你的代码,至关重要。

如何定义理解?
理解是一个非常高的标准。真正理解了,就应该能改动它、找出缺陷且明白它与外部代码交互的规则

可读性的标准可以降低吗?

当可读性和其他约束条件冲突时,比如性能、代码行数,如何取舍?

大部分情况,可读性优先,那些可能会经常被他人阅读、改动的代码,可读性再怎么强调都不为过。

编写可读性的代码很难吗?

编写可读性高的代码很难。如果一个程序员放弃了可读性这一目标,那么他一定不会成为更好的程序员。
编写可读性高的代码,前人已经总结了诸多技巧和经验,学习并实践这些经验,可以让代码的可读性不至于很糟糕。

取一个见名知义的名字

好的代码,从好的命名开始。

把信息放在名字里

选择专业的词汇,使得意思更加清晰和精确。

比如 fetchDatagetData 好;

单词更多的选择
senddeliver、dispatch(派发)、announce(声明)、distribute(分配、广播)、route(按照指定路径投送)
findsearch、extract(提取)、locate(定位)、recover(还原)
startlaunch、create(创建)、begin、open
makecreate、setup(就绪)、build、generate(生成)、compose、add、new

技巧:如果存在两个相对或者相反的操作,取名使最好能让他人知道一个,便能直接的猜到有一个操作。

startstop 相对, pauseresume 相对, includeexclude 相对。

技巧:取名符合生活或者数学的习惯。比如 count 一般为正数, num 使可正可负, complexNumber 复数。

避免宽泛的名字,除非有特别的理由

避免使用 tmpretval 这类宽泛的名字。好的名字应该描述变量的目的或者它承载的值tmp_file 比如 tmp 好。

const euclidean_norm = arr => {
  let retval = 0
  for (let i = 0 i < arr.length; i++) {
    retval += arr[i] * arr[i]
  }
  return Math.sqrt(retval)
}

这函数在累加数组中元素的平方 ,把 retval 改成 sum_squares 更好。

sum_squares 包含它的目的,如果累加代码不小心写成 retval += arr[i] ,就非常容易发现缺陷。

使用 tmp、it 和 retval 等这些空范的名字时,你需要有足够好的理由。

使用具体的名字,避免抽象的名字

力求把实体描述得更具体,而非抽象,越具体,会越明确。
serverCanStart() 没有 canListenOnPort 具体。

技巧:名字里名字和形容词时,会比较具体。感觉不够具体时,不妨加入名字和形容词。

使用前缀或者后缀把重要的信息附加到名字上

常见的可以附加的信息:


① 单位

函数参数参数带单位
start(delay)delay -> delay_secs
createCache(size)size -> size_mb
throttleDownload(limit)limit -> max_kbps
rotate(angle)angle -> degrees_cw

angle 角度,单位度。cw(circular_measure),弧度。

在一个项目遇到一个函数的参数对象属性为 rotate :

someFunction({
  rotate: rotate_value
})

它接收一个从后台接口返回的值,采用的单位是度,产品经理一直说不对,但是我们也找不到问题,就把这个问题放了很久。产品经理有一天又去找人确认是否正确,给的答复没问题。


产品又来找我,说那边反馈数据对的。我才猛然想到采用的单位是不是弧度,于是我把角度转成了弧度,产品就说对了。如果给属性加上单位,那么就一眼看出来了。

我们这个项目还涉及角度的方向,最后几经测试,需要做两件事情:角度转为弧度;三维地图下,旋转方向为逆时针。

把单位加入参数。

someFunction({
  route_cw: cw_value // 顺时针的弧度 anticlockwise/counterclockwise 逆时针
})

如果把三维地图下逆时针也加入,参数就长了,选择不加,可通过函数注释的方式告知使用者。

② 变量存在危险或者意外

情形变量更好的选择
纯文本的密码passwordplaintext_password
用户提供的注释,需要转义后才能显示commentunescaped_comment
安全的 html 代码htmlhtml_safe\ html_escaped
变量作用域大小决定名字长短

小作用域,使用短名字,大作用域,使用长名字。

const numList = [1, 2, 3]
let sum = 0
for (let i = 0; i < array.length; i++) {
  sum += array[i];
}

i 的作用域很小,即使取名很短,也一眼能看出它的目的。

再看一例:

for (i = 0; i < clubs.size(); i++) {
  for (j = 0; j < clubs[i].members.size(); j++) {
    for (k = 0; k < users.size(); k++) {
      // do something
    }
  }
}

i j k 的作用域也很小,但是这里涉及到多层嵌套,当嵌套里操作复杂时,就很容易混淆它们,此时可以适当让各自的下标变长,以做区分。

for (club_i = 0; club_i < clubs.size(); club_i++) {
  for (member_i = 0; member_i < clubs[club_i].members.size(); member_i++) {
    for (user_i = 0; user_i < users.size(); user_i++) {
      // do something
    }
  }
}

长名字不好打,而不使用?

有人会因为长名字需要输入更多字符而不想使用,现在而 IDE、AI 编程助手已经能自动补全了,不存在这个问题。

避免随意缩写,造单词。

随意缩写很让人费解。

技巧:开启编辑器拼写检查,可防止写错单词。

众所周知的缩写是可以使用的,比如

# 名词和形容词
button -> btn
background -> bg
backgroundColor -> bgColor
image -> img
document -> doc
string -> str
number -> num
evaluation -> eval
index -> i
column -> col
hexadecimal -> hex
binary -> bin
octal -> oct # 八进制
decimal -> dec # 十进制
to -> 2 # to 在中间可,可缩写为 2, 比如 bin2dec 二进制转为十进制
address -> addr
application -> app
average -> avg # 平均数
command -> cmd
organization -> org # 组织
original -> orig
destination -> dest/des
resource -> res # 资源
source -> src # 源数据
ascending -> asc # 升序
descending -> desc # 降序
device -> dev # 设备
different -> diff # 区别
directory -> dir # 目录
environment -> env # 环境
error -> err
library -> lib # 库
information -> info # 信息
message -> msg
number -> num
object -> obj
parameter -> param
parameters -> params # 实参 参数
arguments -> args # 实参 参数
argument -> arg # 形参 参数
package -> pkg # 包 n 打包 v
position -> pos # 位置
configuration -> config # 配置
array -> arr
previous -> pre
next -> next
middle -> mid # 中间
current -> cur # 当前的
password -> pwd
reference -> ref
summation -> sum
system -> sys # 系统
temporary -> temp # 临时 或者 tmp
text -> txt # 纯文本
variable -> var
character -> char
status -> stat # 状态
standard -> std # 标准
trigger -> trig # 触发
user -> usr # 用户
length -> len # 长度
administrator -> adm # 管理员
database -> db # 数据库
coordinates -> coord # 坐标
longitude -> lng # 经度
longitude -> lgtd # 经度
latitude -> lat # 维度
latitude -> lttd # 维度
angle -> ng # 角度
circularMeasure -> cw # 弧度
dictionary -> dic # 字典
link -> lnk # 链接
window -> win/wnd # 窗口
horizontal -> horz # 水平
public -> pub
accumulator -> acc # 累加器

# 动词
delete -> del
decrease -> desc # 减少
increase -> inc # 增加
increment -> inc # 增加
execute -> exec # 执行
maximum -> max # 最大
minimum -> min # 最小
calculate -> calc
initialization -> init # 初始化的
initialize -> init # 初始化
generate -> gen # 生成
synchronization -> sync
asynchronization -> async
control -> ctr # 控制
escape -> esc # 退出
insert -> ins # 插入
extend -> ex/ext # 扩展
vertical -> vert # 垂直
instance -> ins # 实例
multiply -> mul # 乘
include -> incl # 包含
exclude -> excl # 排除

禁止拼音缩写

拼写的缩写意思很多。

# 书本
shuBen -> sb
# 想表示 book,却变成傻逼
# 价格
jiaGe -> jg
# 想表示 price,却变成鸡哥

使用缩写的经验法则:当新成员能理解这个缩写时,即可使用。

删除没用的词

拿掉某个词,不会损失信息,就删除它。
比如

convertToStr -> toStr # 意思一样
doStop -> stop #

有多个意思一样的动词,往往只需保留一个。

使用格式表示含义

把特定的格式放在变量里,使得一眼能看出不同变量的区别。
常用的格式有: _-#$大小写 等。

const YYYYMMDD = '2023-03-23' // 这个变量,就能一眼推断出表示一个时间

遵循编程语言或者团队的约定,且保持一致。
对前端来说,构造函数使用大坨峰(PascalCase/UpperCamelCase),普通函数使用小驼峰(lowerCamelCase)。

const person = new Person()
const age = getAge()

jquery 对象使用 $ 开头,事件处理器使用 onXxx 或者 handleXxx

技巧: onXxx 作为属性或者参数,而 handleXxx 作为函数,会更好。

const $allImages = $('img')

DOM 的 ID 属性值使用 _ 连接,类名使用 - 或者 --

<span class="icon-container" id="icon_edit"></span>

vue 和 react 组件中: _ref 表示模板引用:

const div_ref = ref() // 表示引用一个 div
const com_ref = ref() // 表示引用一个 组件

不要使用有歧义的名字

名字容易歧义吗?当命名时,多问问自己,主动找到可能歧义的词语。
filter 就是一样容易有歧义的词语,可以是挑出一些,也可以是删除一些,剩下一些。

minmax 表示极限
const CART_TOO_BIG_LIMIT = 10 // 购物车最多10个物品
const MAX_ITEMS_IN_CART = 10 //  ✅  better
firstlast 表示包含末尾
const str = 'abcd' // first = 0  last = 3 包含 d
start/beginend 表示不包含末尾
const str = 'abcd' // start = 0  end = 3 不包含 d
更好地给变量加否定前缀

英文中表示否定前缀的有很多,比如 undedis ,如何选择呢?

前缀后缀描述例子
non名词、动词、形容词表相反的意思editedRows->nonEditedRows
de动词表相反的动作encodeURIComponent->decodeURIComponent
unable 结尾的形容词或者一动词founded->unfounded known -> unknown

non 最通用,搞不清楚,一律 non 就行了。

参考: How to Use the Prefixes “Dis” and “Un” Correctly

给布尔变量命名

当命名布尔变量或者返回布尔值的函数命名时,要确保阅读者一眼明确(返回)值的范围是 true 或者 false

const read_password = true // bad ❌ 两种理解:1. 读取密码,动作 2. 已经读取了密码
const need_password = true // ok 

标签:function,const,可读性,代码,storage,value,return,编写
From: https://blog.csdn.net/JackZhouMine/article/details/140093440

相关文章

  • 图神经网络版本的Kolmogorov Arnold(KAN)代码实现和效果对比
    KolmogorovArnoldNetworks(KAN)最近作为MLP的替代而流行起来,KANs使用Kolmogorov-Arnold表示定理的属性,该定理允许神经网络的激活函数在边缘上执行,这使得激活函数“可学习”并改进它们。目前我们看到有很多使用KAN替代MLP的实验,但是目前来说对于图神经网络来说还没有类似的实验......
  • 一个适合用来搭建资源网站的整站打包代码,集成了全开源的ripro主题,效果非常棒
    概述发现了一个适合用来搭建资源网站的整站打包代码,集成了全开源的ripro主题,效果非常棒。用d盾扫描过代码,没有发现后门,没有加密文件。这个资源包里自带上千条高质量源码资源数据(数据都比较新,不是那种好几年前的老数据),搭建一个资源网站是绰绰有余了。基于wordpress+ripro主......
  • git同一分支代码冲突 [GPT]
    问题描述李四和王五同时下载了dev分支代码,main.cpp代码为:#include<stdio.h>voidmain(void){ printf("hello");}李四修改代码后提交并push,李四修改的代码#include<stdio.h>voidmain(void){ printf("hello李四");}同时王五也修改了代码#include<stdio.h>voi......
  • 【译】在调试时轻松导航代码委托
    委托在现代代码中无处不在;委托是一种类型,它表示对具有特定参数列表和返回类型的方法的引用。开发人员使用委托将方法作为参数传递给其他方法。您可能熟悉的一个例子是事件处理程序。处理程序是可以通过委托调用的方法。委托让我想起C++的函数指针,当然委托是完全面向对象的。......
  • 一款利用人工智能将自然语言查询转换为 SQL 代码的互译工具 - SQL Translator
    https://www.sqltranslate.app/ 一款利用人工智能将自然语言查询转换为SQL代码的互译工具-SQLTranslator 思维导航前言SQLTranslator介绍工具特性本地项目部署在线效果演示程序员常用的工具软件前言对于后端程序员来说,编写SQL代码是日常工作中不可或缺......
  • 代码随想录算法训练营第九天|232.用栈实现队列、225.用队列实现栈、 20.有效的括号、1
    文章目录232.用栈实现队列思路--直接模拟225.用队列实现栈解法一、两个队列模拟解法二、一个队列模拟20.有效的括号栈模拟1047.删除字符串中的所有相邻重复项解法一、栈解法二、双指针232.用栈实现队列题目链接:232.用栈实现队列-力扣(LeetCode)题目描述:请你仅......
  • Appium+python自动化(三十三)- 本地调试测试代码,远程控制测试环境-Remote(超详解)
    简介在前边所有涉及启动app的时候有这样一行代码driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps),很多小伙伴们和同学们不知道这个ip和端口哪里来的,我觉得有必要给小伙伴解释一下,于是宏哥决定写一篇关于这个appium的服务器ip文章!来给大家答答疑......
  • 代码随想录算法训练营Day9 | 字符串 151.翻转字符串单词 28.实现strStr() KMP算法介绍
    python中常用:        s[::-1]: 反转整个字符        s.strip():删除开头或结尾处的空白字符     s.split():字符拆分成单词 →list    “”.join(s):list→字符串   (持续更新…) 151.翻转字符串里的单词 题目: Leetcod......
  • 2024年全球最好的低代码开发平台有哪些
    什么是低代码开发平台?低码开发平台是一个应用程序,提供图形用户界面编程,从而以非常快的速度开发代码,减少了传统的编程工作。这些工具有助于快速开发代码,最大限度地减少手工编码的努力。这些平台不仅有助于编码,而且还能快速安装和部署。低码开发工具的好处由frevvo调研显......
  • leetcode 常见题型代码总结
    二分查找classSolution(object):defsearch(self,nums,target):""":typenums:List[int]:typetarget:int:rtype:int"""left,right=0,len(nums)-1whileleft<......