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;
}
// 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;
}
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

阅读余下内容
 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注


京ICP备12002735号