CategoryResourceRepost/极客时间专栏/设计模式之美/设计原则与思想:规范与重构/31 | 理论五:让你最快速地改善代码质量的20条编程规范(上).md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

164 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="31 | 理论五让你最快速地改善代码质量的20条编程规范" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/35/d9/35dcf295c6cbc3514ce0e6d98ca717d9.mp3"></audio>
前面我们讲了很多设计原则,后面还会讲到很多设计模式,利用好它们可以有效地改善代码质量。但是,这些知识的合理应用非常依赖个人经验,用不好有时候会适得其反。而我们接下来要讲的编码规范正好相反。编码规范大部分都简单明了,在代码细节方面,能立竿见影地改善质量。除此之外,我们前面也讲到,持续低层次、小规模重构依赖的基本上都是编码规范,这也是改善代码可读性的有效手段。
关于编码规范、如何编写可读代码很多书籍已经讲得很好了我在前面的加餐中也推荐过几本经典书籍。不过这里我根据我自己的开发经验总结罗列了20条我个人觉得最好用的编码规范。掌握这20条编码规范能你最快速地改善代码质量。因为内容比较多所以我分为三节课来讲解分别介绍编码规范的三个部分命名与注释Naming and Comments、代码风格Code Style和编程技巧Coding Tips
## 命名
大到项目名、模块名、包名、对外暴露的接口,小到类名、函数名、变量名、参数名,只要是做开发,我们就逃不过“起名字”这一关。命名的好坏,对于代码的可读性来说非常重要,甚至可以说是起决定性作用的。除此之外,命名能力也体现了一个程序员的基本编程素养。这也是我把“命名”放到第一个来讲解的原因。
取一个特别合适的名字是一件非常有挑战的事情,即便是对母语是英语的程序员来说,也是如此。而对于我们这些英语非母语的程序员来说,想要起一个能准确达意的名字,更是难上加难了。
实际上命名这件事说难也不难关键还是看你重不重视愿不愿意花时间。对于影响范围比较大的命名比如包名、接口、类名我们一定要反复斟酌、推敲。实在想不到好名字的时候可以去GitHub上用相关的关键词联想搜索一下看看类似的代码是怎么命名的。
那具体应该怎么命名呢好的命名有啥标准吗接下来我就从4点来讲解我的经验。
### 1.命名多长最合适?
在过往的团队和项目中,我遇到过两种截然不同的同事。有一种同事特别喜欢用很长的命名方式,觉得命名一定要准确达意,哪怕长一点也没关系,所以,这类同事的项目里,类名、函数名都很长。另外一种同事喜欢用短的命名方式,能用缩写就尽量用缩写,所以,项目里到处都是包含各种缩写的命名。你觉得这两种命名方式,哪种更值得推荐呢?
在我看来,尽管长的命名可以包含更多的信息,更能准确直观地表达意图,但是,如果函数、变量的命名很长,那由它们组成的语句就会很长。在代码列长度有限制的情况下,就会经常出现一条语句被分割成两行的情况,这其实会影响代码可读性。
实际上在足够表达其含义的情况下命名当然是越短越好。但是大部分情况下短的命名都没有长的命名更能达意。所以很多书籍或者文章都不推荐在命名时使用缩写。对于一些默认的、大家都比较熟知的词我比较推荐用缩写。这样一方面能让命名短一些另一方面又不影响阅读理解比如sec表示second、str表示string、num表示number、doc表示document。除此之外对于作用域比较小的变量我们可以使用相对短的命名比如一些函数内的临时变量。相反对于类名这种作用域比较大的我更推荐用长的命名方式。
总之,命名的一个原则就是以能准确达意为目标。不过,对于代码的编写者来说,自己对代码的逻辑很清楚,总感觉用什么样的命名都可以达意,实际上,对于不熟悉你代码的同事来讲,可能就不这么认为了。所以,命名的时候,我们一定要学会换位思考,假设自己不熟悉这块代码,从代码阅读者的角度去考量命名是否足够直观。
### 2.利用上下文简化命名
我们先来看一个简单的例子。
```
public class User {
private String userName;
private String userPassword;
private String userAvatarUrl;
//...
}
```
在User类这样一个上下文中我们没有在成员变量的命名中重复添加“user”这样一个前缀单词而是直接命名为name、password、avatarUrl。在使用这些属性时候我们能借助对象这样一个上下文表意也足够明确。具体代码如下所示
```
User user = new User();
user.getName(); // 借助user对象这个上下文
```
除了类之外,函数参数也可以借助函数这个上下文来简化命名。关于这一点,我举了下面这个例子,你一看就能明白,我就不多啰嗦了。
```
public void uploadUserAvatarImageToAliyun(String userAvatarImageUri);
//利用上下文简化为:
public void uploadUserAvatarImageToAliyun(String imageUri);
```
### 3.命名要可读、可搜索
首先,我们来看,什么是命名可读。先解释一下,我这里所说的“可读”,指的是不要用一些特别生僻、难发音的英文单词来命名。
过去我曾参加过两个项目一个叫plateaux另一个叫eyrie从项目立项到结束自始至终都没有几个人能叫对这两个项目的名字。在沟通的时候每当有人提到这两个项目的名字的时候都会尴尬地卡顿一下。虽然我们并不排斥一些独特的命名方式但起码得让大部分人看一眼就能知道怎么读。比如我在Google参与过的一个项目名叫inkstone虽然你不一定知道它表示什么意思但基本上都能读得上来不影响沟通交流这就算是一个比较好的项目命名。
我们再来讲一下命名可搜索。我们在IDE中编写代码的时候经常会用“关键词联想”的方法来自动补全和搜索。比如键入某个对象“.get”希望IDE返回这个对象的所有get开头的方法。再比如通过在IDE搜索框中输入“**Array**”搜索JDK中数组相关的类。所以我们在命名的时候最好能符合整个项目的命名习惯。大家都用“selectXXX”表示查询你就不要用“queryXXX”大家都用“insertXXX”表示插入一条数据你就要不用“addXXX”统一规约是很重要的能减少很多不必要的麻烦。
### 4.如何命名接口和抽象类?
对于接口的命名一般有两种比较常见的方式。一种是加前缀“I”表示一个Interface。比如IUserService对应的实现类命名为UserService。另一种是不加前缀比如UserService对应的实现类加后缀“Impl”比如UserServiceImpl。
对于抽象类的命名也有两种方式一种是带上前缀“Abstract”比如AbstractConfiguration另一种是不带前缀“Abstract”。实际上对于接口和抽象类选择哪种命名方式都是可以的只要项目里能够统一就行。
## 注释
命名很重要,注释跟命名同等重要。很多书籍认为,好的命名完全可以替代注释。如果需要注释,那说明命名不够好,需要在命名上下功夫,而不是添加注释。实际上,我个人觉得,这样的观点有点太过极端。命名再好,毕竟有长度限制,不可能足够详尽,而这个时候,注释就是一个很好的补充。
### 1.注释到底该写什么?
注释的目的就是让代码更容易看懂。只要符合这个要求的内容,你就可以将它写到注释里。总结一下,注释的内容主要包含这样三个方面:做什么、为什么、怎么做。我来举一个例子给你具体解释一下。
```
/**
* (what) Bean factory to create beans.
*
* (why) The class likes Spring IOC framework, but is more lightweight.
*
* (how) Create objects from different sources sequentially:
* user specified object &gt; SPI &gt; configuration &gt; default object.
*/
public class BeansFactory {
// ...
}
```
有些人认为注释是要提供一些代码没有的额外信息所以不要写“做什么、怎么做”这两方面在代码中都可以体现出来只需要写清楚“为什么”表明代码的设计意图即可。我个人不是特别认可这样的观点理由主要有下面3点。
- 注释比代码承载的信息更多
命名的主要目的是解释“做什么”。比如void increaseWalletAvailableBalance(BigDecimal amount)表明这个函数用来增加钱包的可用余额boolean isValidatedPassword表明这个变量用来标识是否是合法密码。函数和变量如果命名得好确实可以不用再在注释中解释它是做什么的。但是对于类来说包含的信息比较多一个简单的命名就不够全面详尽了。这个时候在注释中写明“做什么”就合情合理了。
- 注释起到总结性作用、文档的作用
代码之下无秘密。阅读代码可以明确地知道代码是“怎么做”的,也就是知道代码是如何实现的,那注释中是不是就不用写“怎么做”了?实际上也可以写。在注释中,关于具体的代码实现思路,我们可以写一些总结性的说明、特殊情况的说明。这样能够让阅读代码的人通过注释就能大概了解代码的实现思路,阅读起来就会更加容易。
实际上对于有些比较复杂的类或者接口我们可能还需要在注释中写清楚“如何用”举一些简单的quick start的例子让使用者在不阅读代码的情况下快速地知道该如何使用。
- 一些总结性注释能让代码结构更清晰
对于逻辑比较复杂的代码或者比较长的函数,如果不好提炼、不好拆分成小的函数调用,那我们可以借助总结性的注释来让代码结构更清晰、更有条理。
```
public boolean isValidPasword(String password) {
// check if password is null or empty
if (StringUtils.isBlank(password)) {
return false;
}
// check if the length of password is between 4 and 64
int length = password.length();
if (length &lt; 4 || length &gt; 64) {
return false;
}
// check if password contains only a~z,0~9,dot
for (int i = 0; i &lt; length; ++i) {
char c = password.charAt(i);
if (!((c &gt;= 'a' &amp;&amp; c &lt;= 'z') || (c &gt;= '0' &amp;&amp; c &lt;= '9') || c == '.')) {
return false;
}
}
return true;
}
```
### 2.注释是不是越多越好?
注释太多和太少都有问题。太多,有可能意味着代码写得不够可读,需要写很多注释来补充。除此之外,注释太多也会对代码本身的阅读起到干扰。而且,后期的维护成本也比较高,有时候代码改了,注释忘了同步修改,就会让代码阅读者更加迷惑。当然,如果代码中一行注释都没有,那只能说明这个程序员很懒,我们要适当督促一下,让他注意添加一些必要的注释。
按照我的经验来说,类和函数一定要写注释,而且要写得尽可能全面、详细,而函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码的可读性。
## 重点总结
好了,今天的内容到此就讲完了。我们来一块总结回顾一下,你需要掌握的重点内容。
**1.关于命名**
- 命名的关键是能准确达意。对于不同作用域的命名,我们可以适当地选择不同的长度。作用域小的变量(比如临时变量),可以适当地选择短一些的命名方式。除此之外,命名中也可以使用一些耳熟能详的缩写。
- 我们可以借助类的信息来简化属性、函数的命名,利用函数的信息来简化函数参数的命名。
- 命名要可读、可搜索。不要使用生僻的、不好读的英文单词来命名。除此之外,命名要符合项目的统一规范,不要用些反直觉的命名。
- 接口有两种命名方式一种是在接口中带前缀“I”另一种是在接口的实现类中带后缀“Impl”。对于抽象类的命名也有两种方式一种是带上前缀“Abstract”一种是不带前缀。这两种命名方式都可以关键是要在项目中统一。
**2.关于注释**
- 注释的目的就是让代码更容易看懂。只要符合这个要求的内容,你就可以将它写到注释里。总结一下,注释的内容主要包含这样三个方面:做什么、为什么、怎么做。对于一些复杂的类和接口,我们可能还需要写明“如何用”。
- 注释本身有一定的维护成本,所以并非越多越好。类和函数一定要写注释,而且要写得尽可能全面、详细,而函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码可读性。
## 课堂讨论
1. 在讲到“用总结性注释让代码结构更清晰”的时候我们举了一个isValidPassword()函数的例子,在代码可读性方面,这个函数还有哪些可以继续优化的地方呢?
1. 关于注释,你推荐使用英文还是中文来书写呢?理由是什么呢?
欢迎在留言区写下你的答案,和同学一起交流和分享。如果有收获,也欢迎你把这篇文章分享给你的朋友。