如何命名可能返回Optional 的查询方法

44 阅读4分钟

在上一篇文章中,我概述了对Java 8的Optional 类的实用方法。在这篇文章中,我将研究我们应该如何命名可能返回Optional 的查询方法。

方便的方法命名

考虑一个产生树状数据结构的需求,也许是类似XML DOM的东西。基本的数据结构可能看起来像这样:

 public XmlElement {
   private final String name;
   private final String content;
   private final Map<String, String> attributes;
   private final List<XmlElement> children;
 }

(本文以此为例,但许多其他的数据结构或领域对象也面临着类似的问题,所以不要过于纠结于这个例子的 "XML "性,它无法代表XML的每一个微妙之处。)

这篇文章的关键问题是关于这个类所暴露的便利方法。 使用标准的最佳实践,一个没有孩子的元素应该返回一个空列表,一个没有属性的元素应该返回一个空图。 同样,当没有内容时,我们可以使用空字符串,"":

 public XmlElement {
   public String getName() {...}
   public String getContent() {...}
   public Map<String, String> getName() {...}
   public List<XmlElement> getChildren() {...}
 }

这种方法很好,可以让我们完全查询这个不可变的数据结构。 但如果能有一些额外的方便方法就更好了。 主要考虑的是一个按名称返回属性值的方法,天真的实现是:

 public XmlElement {
   public String getAttribute(String name) {
     return attributes.get(name);
   }
 }

但是,这个方法可以返回null ,上一篇文章建议不要这样做。不那么天真的实现是:

 public XmlElement {
   public Optional<String> getAttribute(String name) {
     return Optional.ofNullable(attributes.get(name));
   }
 }

这样做更好,因为不再返回null ,而且现在很清楚有一个失败的情况需要考虑。但是,如果调用代码知道它所要求的数据应该在那里,而且如果不在那里就会抛出一个异常怎么办?

在这种情况下,"标准 "的做法是在上一个方法的结果上使用Optional.orElseThrow() 。这是一个很好的建议。但是,同样的,如果在对orElseThrow() 的相同调用之后,又对新的辅助方法进行了大量的调用,这可能是一个迹象,表明这个方便方法并没有起到应有的作用!因此,也许它本身就值得成为一个辅助方法:

 public XmlElement {
   public String getAttribute(String name) {
     String value = attributes.get(name);
     if (value == null) {
       throw new IllegalArgumentException(
           "Unknown attribute '" + name + "' on element '" + this.name + "'"));
     }
     return value;
   }
 }

(注意,这遵循了上一篇文章的建议,在类的方法中使用null ,而不是Optional )。

因此,这对许多用例来说是比较好的,在这些用例中,如果属性缺失,无论如何都是一个失败的案例。也就是说,调用者知道这个属性被定义为强制性的,如果它缺失,必然是一个异常。

但在现实中,有些属性是必须的,有些是可选的。 这就把我们带到了本文的重点,以及针对这种情况的设计方法,还有随之而来的命名问题。 我的建议是有两个方法。

 public XmlElement {

   // throw exception if no attribute
   public String 

在这种方法中,有两个方法,如果调用者想在找不到名字时出现异常,可以选择一个方法,如果想处理丢失的情况,可以选择另一个方法。

对这种方法的需求是比较少的--它需要是一个可以以两种方式使用的方法,强制的和可选的。 它还需要是一个会被足够多地调用的API,以使两个辅助方法的价值超过额外的成本。 但是当它确实出现的时候,它需要一个好的命名规则,因为这两个方法不能是重载, 而这就是我在这里的提议:

  • 当没有找到时,命名为抛出一个异常的方法getXxx(String)
  • 命名返回一个可选的findXxx(String)的方法

在我看来,当这种情况出现时,强制的情况通常是最常见的,因此它被命名为 "get"。

对于这种 "近似重载",还有其他可能的命名方法,但我已经确定了这个方法,作为我的API的正确平衡。

但是请注意,这并不是建议将所有返回Optional 的方法都命名为findXxx(String) 。只有在适当的时候使用,比如与抛出异常的版本配对时,我认为这才有意义。

总结

本文概述了一种命名方法,当你需要在API上使用重载的方便方法时,通常用于访问一个集合,如地图或列表。它建议添加一个方法,getXxx(String) ,在找不到键的时候抛出一个异常,另一个方法findXxx(String) ,在找不到键的时候返回一个空的可选项。