4 个小技巧大幅提高源代码可读性
一篇优秀的文章在阐述观点时,目标受众能够轻松掌握并消化难以理解的概念。编码与撰写文章并无不同。计算机程序代码不仅仅是供机器执行的一组指令和逻辑。软件工程师也是目标受众。
试想,一个天才的软件工程师实现了一个高效而复杂的算法,但遗憾的是,如果没有人能理解其中的逻辑,那么这段代码就是垃圾。虽然人工智能自动化的应用非常普遍,但它的功能还不足以接管整个软件开发和维护工作。换句话说,现在的软件程序代码基本上都是由人来维护的。
因此,无论您是单独开发人员还是在开发团队中工作,以条理清晰、可读性强的格式编码都是一项基本技能。人类的可读性是决定代码质量的关键因素之一。
事实上,编写可读代码的好处是巨大的。代码越容易阅读,人们就能越快地理解系统逻辑。因此,在构建或修改系统功能上花费的时间和精力就越少。最终,缩短产品上市时间,降低软件维护成本。
下面的示例代码是旧式代码:
Employee employee = new Employee();
employee.setId(1);
employee.setName(“Denis Rogers”);
employee.setDepartment(“FINANCE”);
employee.setSalary(1500);
如果我们把上面的代码翻译成通俗易懂的英语,下面是一些句子。表述笨拙且重复。如果在雇员记录中添加更多信息,阅读时间会更长:
创建一名新员工。
雇员 id 为 1。
员工姓名是 Denis Rogers。
员工所在部门为财务部。
员工工资为 15000。
让我们看看相同设置的现代编码风格:
Employee employee = Employee.builder()
.id(1)
.name(“Denis Rogers”)
.department(“FINANCE”)
.salary(1500)
.build();
上述编码风格流畅地呈现了新员工的各项属性。呈现格式类似于下面的纯英文加要点。显然,这种编码方式可以消除冗余代码,更有效地展示信息。
新员工信息
* 编号1
* 姓名丹尼斯-罗杰斯
* 部门:财务部
* 工资: 1500
为了帮助你将编码技能提升到一个新的水平,我将在本文中分享如何编写高质量、易读的代码的 4 个超棒技巧。
技巧 #1–从高层次的流程开始
将所有内容都塞进 Powerpoint 幻灯片,会给受众带来很多信息。许多人认为这是一种有效的方式,可以在完整的上下文中传达你的想法。这是许多演示者常犯的错误,他们把整个段落塞进幻灯片,并认为读者/听众能够完全消化这些信息。事实上,很少有人会因为信息过载而接收到你的信息。
设身处地为读者着想。如果对某一特定领域一无所知,就很难一下子理解所有内容。
编程也是如此。假设学习系统逻辑的人没有任何背景知识。首先,介绍整体情况是帮助读者理解高层流程的重要信息。
让我们来做一个快速测试。如果给你 3 秒钟时间,你能说出下面示例代码中的系统逻辑吗?
public boolean openAccount(AccountOpeningRequest request) {
if (StringUtils.isNotBlank(request.getCustomerName())
&& StringUtils.isNotBlank(request.getCustomerIdentityType())
&& StringUtils.isNotBlank(request.getCustomerIdentityNumber())
&& StringUtils.isNotBlank(request.getNationality())
&& StringUtils.isNotBlank(request.getCustomerAddress())) {
if (request.getCustomerIdentityType().equalsIgnoreCase("DRIVER_LICENSE")
&& request.getCustomerIdentityNumber().length() != 9) {
return false;
}
if (request.getNationality().equalsIgnoreCase("Arabia Terra")
&& request.getCustomerAddress().equalsIgnoreCase("Mars")) {
return false;
}
} else {
return false;
}
if (request.getAccountNumber().startsWith("099")) {
if (!request.getNationality().equalsIgnoreCase("WONDERLAND")) {
return false;
}
if (!request.getAccountType().equalsIgnoreCase("PENSION")) {
return false;
}
amlService.checkForBlacklistedCustomer(request.getCustomerIdentity());
}
Customer customer = customerDao.retrieveCustomerByIdentity(
request.getCustomerIdentityType(), request.getCustomerIdentityNumber());
accountDao.insertAccount(new Account(request.getAccountNumber(), request.getAccountType(),
customer.getId(), Instant.now()));
if (request.getAccountNumber().startsWith("022")) {
investmentService.registerInvestmentAccount(request);
}
return true;
}
别紧张,这不是编码测试练习。事实上,我们需要花时间去理解整个系统的逻辑,因为我们需要仔细研究每一行,弄清流程。
“总结 “是写作和编码中常用的重要技巧。
通过 “总结 “程序代码,整体流程会更加清晰。将详细逻辑移至私有方法中。如果不深入研究详细的系统逻辑,源代码研究就会容易得多。如果新的增强功能需要添加新的验证逻辑,该怎么办?很简单,只需跳转到 validateRequest()
即可,无需理会系统逻辑的其他部分。
public boolean openAccount(AccountOpeningRequest request) {
if (!validateRequest(request)) {
return false;
}
if (!checkRequestForPensionAccountType(request)) {
return false;
}
createNewAccount(request);
handlingForInvestmentAccount(request);
return true;
}
技巧 #2–用人类语言编码
// 099 prefix is pension account
if (request.getAccountNumber().startsWith("099")) {
if (!request.getNationality().equalsIgnoreCase("WONDERLAND")) {
return false;
}
…
…
}
过去,程序员需要在源代码中编写注释,作为编程任务的一部分,以帮助他人遵循系统流程。带有描述性注释的源代码被认为是高质量的。
如今,少即是多,少注释或无注释的可读源代码也被认为是高质量的。
等等!不写注释如何在源代码中记录逻辑?
有意义的方法和变量名
使用人类语言中的方法名是回答这个问题的一个很酷的技巧。下面的示例代码是对 if 语句的一个小调整。如果我们将帐号前缀检查移到名为 isPensionAccount()
的方法中,代码逻辑就会变得清晰明了。有兴趣了解确定养老金账户逻辑的人可以查看该方法的实现。
您的团队会喜欢您的源代码,因为它接近自然语言,易于阅读。
除了将逻辑封装到具有有意义名称的方法中,这种技术还可以应用到变量名称中。
if (isPensionAccount(request)) {
if (!request.getNationality().equalsIgnoreCase("WONDERLAND")) {
return false;
}
…
…
}
有意义的比较
说到数值比较,大多数编程语言都能很好地使用整数和双倍等原始数据类型。运算符 >、=>、<、<= 和 == 的使用清楚地表明了比较操作。
但是,这些运算符不能用于对象的比较。BigDecimal 就是一个典型的例子。我们依靠 compareTo() 在两个值之间进行比较。比较结果将产生一个整数值。下面的示例代码解释了它是如何工作的:
- ZERO – 两个值相等
- > ZERO – 值 A 大于值 B
- < ZERO – A 值小于 B 值
void compareBigDecimal(BigDecimal valueA, BigDecimal valueB) {
int result = valueA.compareTo(valueB);
if (result == 0) {
System.out.println("valueA is equal to valueB");
} else if (result > 0) {
System.out.println("valueA is greater than valueB");
} else {
System.out.println("valueA is smaller than valueB");
}
}
这实际上是对 C/C++ 等低级编程语言的继承。有时,由于表述方式与人类语言不兼容,人们会混淆并粗心地混淆结果。
Apache common lang3 库提供了一个简单的技巧,让这种比较变得易读。该库以流畅的风格提供了以下方法调用。方法名称描述清晰,肯定不会再有混淆和误解。
boolean isEqual = is(valueA).equalTo(valueB);
boolean isGreater = is(valueA).greaterThan(valueB);
boolean isGreaterThanOrEqualTo = is(valueA).greaterThanOrEqualTo(valueB);
boolean isLess = is(valueA).lessThan(valueB);
boolean isLessThanOrEqualTo = is(valueA).lessThanOrEqualTo(valueB);
要在系统中包含 Apache common lang3 库,请将此依赖关系添加到 maven pom.xml 中:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
技巧 #3–使用构建器构建对象
在本文开头的演示中,您可能已经了解了对象构造的构造器模式。虽然在许多编程语言中,使用构造器是创建新对象的传统方法,但许多软件工程师发现,由于要在构造器中添加更多属性,代码变得更加难以理解。
让我们看看下面的示例代码,你能解释下面构造函数中每个属性的含义吗?
Employee employee = new Employee(1, “Denis Rogers”, “FINANCE”,
“Senior Officer”,
LocalDate.parse(“2020-02-28”), true,
new BigDecimal(“15000”));
也许,您可以识别记录 ID、员工姓名、部门和职称。我猜没有人知道 “2020-02-28″、”true “和 “15000”。
让我们用下面的构建器模式替换构造器,现在所有属性都解释得很清楚了。这样就不会产生歧义,也避免了混淆构造函数参数的机会。
Employee employee = Employee.builder()
.id(1)
.name(“Denis Rogers”)
.department(“FINANCE”)
.title(“Senior Officer”)
.employmentDate(LocalDate.parse(“2020-02-28”))
.fullTime(true)
.salary(new BigDecimal(“15000”))
.build();
使用 Lombok 库可以毫不费力地完成构建器的模板代码,它可以在字节码级别神奇地生成构建器模式。在下面的示例中加入 Lombok 的注解 @Builder、@Setter 和 @Getter,它就会自动生成 getter 和 setter 方法以及构建器模式。
@Builder
@Setter
@Getter
public class Employee {
private int id;
private String name;
private String department;
private String title;
private LocalDate employmentDate;
private boolean fullTime;
private BigDecimal salary;
}
技巧 #4 – 使用 ?
操作符简化逻辑分支
?
操作符是一个方便的辅助工具,可以简化变量赋值的分支。如果我们要实现一个新功能,在功能标志后面返回一个奖励金额。这是一个使用 if 语句的简单分支逻辑。BigDecimal bonusAmount = BigDecimal.ZERO;
If (featureFlag) {
bonusAmount = retrieveBonusAmount();
}
运算符”?
“的使用使逻辑更加整洁,因为读者无需浏览 if 语句,就能找到变量 bonusAmount 的数据源。在更简单的情况下,变量赋值只需一行代码即可完成。
也许,这个小改动只是稍微提高了代码的可读性。对于阅读成百上千行代码的软件工程师来说,这些微小的改进都会加快他们研究源代码的速度。
BigDecimal bonusAmount = featureFlag ? BigDecimal.ZERO
: retrieveBonusAmount();
最后的思考
当你编写程序代码时,你不仅仅是在编写机器指令。源代码是一份有生命力的文档,会定期发展和改进。将来可能会有人阅读并改进你的源代码。
在源代码中呈现系统逻辑的方式会产生巨大的影响。它会影响其他人吸收重要系统逻辑的速度。与撰写文章类似,如果您能以高效的方式呈现信息,受众就能轻松理解您的想法。
由于系统逻辑清晰易懂,因此调试起来也更容易,隐藏的问题也更少。这些都是高质量源代码的关键因素。这些快速提示总结起来供您快速参考:
- 从高层流程开始
- 用人类语言编码
- 使用生成器构建对象
- 使用 ? 运算符简化逻辑分支
本文文字及图片出自 4 small tips massively improve your source code readability