一篇优秀的文章以一种让目标受众能够轻松理解和消化困难概念的方式呈现想法。编码与写文章并无二致。计算机程序代码不仅仅是一组指令和逻辑,供机器执行。软件工程师也是目标受众。
想象一下,一个天才软件工程师实现了一个高效而复杂的算法,遗憾的是,如果没有人能理解这段逻辑,那这段代码就是垃圾。尽管AI自动化的使用很普遍,但它还不足以完全接管整个软件开发和维护。换句话说,如今软件程序代码主要由人类维护。
因此,以一种良好组织和可读性强的格式编码是一项必不可少的技能,无论您是作为独立开发人员还是在开发团队中工作。人类可读性是决定代码质量的关键因素之一。
事实上,编写可读性强的代码的好处是巨大的。代码越容易阅读,人们就能越快地理解系统逻辑。因此,花费更少的时间和精力来构建或修改系统功能。最终,缩短上市时间,降低软件维护成本。
下面的示例代码是以一种老式风格编写的:
Employee employee = new Employee();
employee.setId(1);
employee.setName("Denis Rogers");
employee.setDepartment("FINANCE");
employee.setSalary(1500);
如果我们将上面的代码翻译成简单的英语句子,会是这样。这种表达方式笨拙而重复。如果要添加更多信息到员工记录中,阅读起来会更耗时:
创建一个新员工。 员工编号为1。 员工姓名为Denis Rogers。 员工所在部门为FINANCE。 员工的薪水为15000。
让我们看看相同设置的现代编码风格:
Employee employee = Employee.builder()
.id(1)
.name("Denis Rogers")
.department("FINANCE")
.salary(1500)
.build();
上面的编码风格流畅地呈现了新员工的每个属性。这种呈现格式类似于下面的英语简述。显然,这种编码风格消除了冗余代码,并更有效地呈现信息。
新员工的信息:
- 编号:1
- 姓名:Denis Rogers
- 部门:FINANCE
- 薪水:1500
为了帮助您将编码技能提升到更高水平,我将在本文中分享4个关于如何生成高质量且易读的代码的绝妙贴士。
将所有内容都扔进一张幻灯片中会给您的受众提供大量信息。许多人认为这是一种有效的方式来传达您的想法并提供完整的上下文。这是许多演讲者犯的一个常见错误,他们将整个段落塞进一张幻灯片中,并假设读者/观众能够完全消化信息。事实是,由于信息过载,很少有人能够接收到您的信息。
设身处地为您的读者着想。如果一个人对特定领域没有任何知识,一次性理解一切是很困难的。
编程也是如此。假设理解系统逻辑的人没有任何背景知识。首先,引入整体图片是帮助您的读者理解高层流程的重要信息。
让我们做一个快速测试。如果给你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;
}
注释是一种软件工程师将源代码中的系统逻辑解释给他人的文档。例如,下面的注释解释了带有前缀"099"的帐号是养老金帐号。
// 099前缀是养老金帐号
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等于valueB");
} else if (result > 0) {
System.out.println("valueA大于valueB");
} else {
System.out.println("valueA小于valueB");
}
}
这实际上是低级编程语言(如C/C++)的继承。有时,人们会因为表达方式不符合人类语言而感到困惑并粗心地混淆结果。
Apache common lang3库提供了一个简单的技巧,使这种比较更易读。该库以流畅的方式提供以下方法调用。方法名称清晰描述,因此不会再有混淆和误解。
Dim isEqual As Boolean = is(valueA).equalTo(valueB)
Dim isGreater As Boolean = is(valueA).greaterThan(valueB)
Dim isGreaterThanOrEqualTo As Boolean = is(valueA).greaterThanOrEqualTo(valueB)
Dim isLess As Boolean = is(valueA).lessThan(valueB)
Dim isLessThanOrEqualTo As Boolean = is(valueA).lessThanOrEqualTo(valueB)
要在系统中包含 Apache common lang3 库,请将以下依赖项添加到 Maven 的 pom.xml 文件中:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
您可能已经了解了在本文开头演示的对象构建中的构建器模式。尽管在许多编程语言中使用构造函数是创建新对象的传统方式,但许多软件工程师发现随着要添加更多属性到构造函数中,代码变得更难理解。
让我们看看下面的示例代码,您能解释下面构造函数中每个属性的含义吗?
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 方法以及构建器模式。有关 Lombok 库的使用,请参阅 如何神奇地加快 Java 编码速度。
@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;
}
?
运算符是一个方便的辅助工具,可以简化变量赋值的分支逻辑。如果我们要实现一个新功能,在一个特性标志后面返回一个奖金金额。这是一个使用 if 语句的简单分支逻辑。
BigDecimal bonusAmount = BigDecimal.ZERO;
If (featureFlag) {
bonusAmount = retrieveBonusAmount();
}
使用 ? 运算符使逻辑更整洁,因为读者能够找出变量 bonusAmount 的数据源,而无需导航 if 语句。对于更简单的情况,变量赋值可以用一行代码完成。
也许,这种小改变只是稍微提高了代码的可读性。这些小的改进累积起来,加快了软件工程师阅读数百甚至数千行代码的速度。
BigDecimal bonusAmount = featureFlag ? BigDecimal.ZERO
: retrieveBonusAmount();
当您编写程序代码时,您不仅仅是在编写机器指令。源代码是一个不断演变和改进的活文档。将来可能会有人阅读和增强您的源代码。
您在源代码中呈现系统逻辑的方式会产生巨大影响。它影响其他人能够多快地吸收重要的系统逻辑。与文章写作类似,如果您以高效的方式呈现信息,您的受众将能够轻松理解您的想法。
由于系统逻辑清晰且易于理解,因此更容易调试,隐藏问题更少。这些是高质量源代码的关键因素。