逸言

使用Guava提供的Preconditions

| Comments

我看到这么一段Java代码,主要逻辑是对传入的数据对象进行分析和处理。方法除了长了一点,本身没有什么问题,但一个不好的味道是方法中充斥了一部分对对象非空的处理,以及写日志的信息。这些语句本身不属于方法的主干实现,却很讨厌地干扰了方法的主要逻辑,影响了程序的可读性。

public class Processor {
  public void execute(ProcessData processData) {
      if (processData == null || processData.getInput() == null) {
          logger.error("processData is error");
          return;
      }

      List<FavoratePolicy> policies = fetchPolicies(processData);
      if (policies == null || policies.isEmpty()) {
          logger.error("can not fetch favorate policies");
          return;
      }

      for (FavoratePolicy policy : policies) {
          //handle policy
      }
  }
}

我最初想要尝试使用Guava提供的Optional。经过仔细分析,发现这里的场景并不适合Optional。Optional确实可以用于处理null object,甚至可以将其看做是Null Object模式的实现。但它为Java语言带来的好处,无非是提高了代码可读性,同时可以有效地防止程序员忘记对null的判断。它不能带来代码量的减少,用在这里,也无法消除非空判断以及日志的噪音。所幸,Guava还提供了Preconditions,它拥有更好而简洁的断言方式,可以处理程序的一些异常分支。它主要定义的方法包括checkArgument(), checkNotNull()以及checkState()等,可以通过抛指定异常的方式,将程序的主分支与异常分支有效地隔离开。通过引入checkArgument()方法,前面的代码可以调整为:

public class Processor {
  public void execute(ProcessData processData) {
      try {
          checkArgument(processData != null && processData.getInput() != null, "processData is error");

          List<FavoratePolicy> policies = fetchPolicies(processData);

          checkArgument(policies != null && (!policies.isEmpty()), "can not fetch favorate policies");
      
          for (FavoratePolicy policy : policies) {
              //handle policy
          }

      } catch (IllegalArgumentException ex) {
          logger.error(ex.getMessage());
      }
  }
}

经过这样的处理,程序的主体逻辑变得清晰了许多,而且也能够有效地避免日志方法的重复。当然,这里也可以使用checkNotNull()方法来处理ProcessData与List的null情形。但由于它抛出的异常为NullPointerException,用在这里会增加一个异常捕获(因为它无法处理isEmpty()的情况),所以就一致地选择了checkArgument()方法。不过,在使用这一方法时,要注意它传入的条件表达式与原有实现的表达式是相反的。

Comments