欢迎光临北大青鸟福州新生代校区

  Java 8 API 设计经验浅析

  任何写Java代码的人都是API设计师!无论编码者是否与他人共享代码,代码仍然被使用:要么其他人或他们自己使用,要么两者皆有。因此,对于所有的Java开发人员来说,了解良好API设计的基础很重要。

  一个好的API设计需要仔细思考和大量的经验。幸运的是,如Ference Mihaly正是他的启发我写了这篇Java 8 API附录那里学习。在设计Speedment API时,我们非常依赖于他列出的接口清单。(我建议大家不妨读一读他的指南。)

  从一开始就做到这一点很重要,因为一旦API发布,就会成为使用API的人坚实的基石。正如Joshua Bloch曾经说过的:“公共API,就像钻石一样永恒久远。你有机会把它做正确的话,就应该竭尽全力去做。

  一个精心设计的API结合了两个世界的精华,既是坚实而的基石,又具有高度的实施灵活性,让API设计师和API使用者受益。

  至于为什么要使用接口清单?正确地获取API(即定义Java类集合的可见部分)比编写构成API背后实际工作的实现类要困难得多。它是一个真的很少有人掌握的艺术。使用接口清单允许读者避免明显的错误,成为更好的和节省大量的时间。

  强烈建议API设计者将自己置于客户端代码的角度,并从简单性,易用性和一致性方面优化这个视图而不是考虑实际的API实现。同时,他们应该尽量隐藏尽可能多的实现细节。

  不要用返回Null来表示一个空值

  可以证明,不一致的null处理(导致无处不在的NullPointerException)是历史上Java应用程序错误的来源。一些开发人员将引入null概念当作是计算机科学领域犯的糟糕的错误之一。幸运的是,减轻Java null处理问题的是在Java 8中引入了Optional类。确保将返回值为空的方法返回Optional,而不是null。

  这向API使用者清楚地表明了该方法可能返回值,也可能不返回值。不要因为性能原因的诱惑使用null而不使用Optional。反正Java 8的转义分析将优化掉大多数Optional对象。避免在参数和字段中使用Optional。

  你可以这样写

  public Optional getComment() {

  return Optional.ofNullable(comment);

  }

  而不要这样写

  public String getComment() {

  return comment; // comment is nullable

  }

  不要将数组作为API的传入参数和返回值

  当Java 5中引入Enum概念时,出现了一个重大的API错误。我们都知道Enum类有一个名为values()的方法,用来返回所有Enum不同值的数组。现在,因为Java框架确保客户端代码不能更改Enum的值(例如,通过直接写入数组),因此得为每次调用value()方法生成内部数组的副本。

  这导致了较差的性能和较差的客户端代码可用性。如果Enum返回一个不可修改的List,该List可以重用于每个调用,那么客户端代码可以访问更好和更有用的Enum值的模型。在一般情况下,如果API要返回一组元素,考虑公开Stream。这清楚地说明了结果是只读的(与具有set()方法的List相反)。

  它还允许客户端代码容易地收集另一个数据结构中的元素或在运行中对它们进行操作。此外,API可以在元素变得可用时(例如,从文件,套接字或从数据库中拉入),延迟生成元素。同样,Java 8改进的转义分析将确保在Java堆上创建实际少的对象。

  也不要使用数组作为方法的输入参数,因为除非创建数组的保护性副本使得有可能另一个线程在方法执行期间修改数组的内容。

  你可以这样写

  public Stream comments() {

  return Stream.of(comments);

  }

  而不要这样写

  public String[] comments() {

  return comments; // Exposes the backing array!

  }

  考虑添加静态接口方法以提供用于对象创建的单个入口点

  避免允许客户端代码直接选择接口的实现类。允许客户端代码创建实现类直接创建了一个更直接的API和客户端代码的耦合。它还使得API的基本功能更强,因为现在我们保持所有的实现类,就像它们可以从外部观察到,而不仅仅只是提交到接口。

  考虑添加静态接口方法,以允许客户端代码来创建(可能为专用的)实现接口的对象。例如,如果我们有一个接口Point有两个方法int x() 和int y() ,那么我们可以显示一个静态方法Point.of( int x,int y) ,产出接口的(隐藏)实现。

  所以,如果x和y都为零,那么我们可以返回一个特殊的实现类PointOrigoImpl(没有x或y字段),否则我们返回另一个保存给定x和y值的类PointImpl。确保实现类位于另一个明显不是API一部分的另一个包中(例如,将Point接口放在com.company。product.shape中,将实现放在com.company.product.internal.shape中)。

  你可以这样写

  Point point = Point.of(1,2);

  而不要这样写

  Point point = new PointImpl(1,2);

  青睐功能性接口和Lambdas的组合优于继承

  出于好的原因,对于任何给定的Java类,只能有一个超类。此外,在API中展示抽象或基类应该由客户端代码继承,这是一个非常大和有问题的API 功能。避免API继承,而考虑提供静态接口方法,采用一个或多个lambda参数,并将那些给定的lambdas应用到默认的内部API实现类。

  这也创造了一个更清晰的关注点分离。例如,并非继承公共API类AbstractReader和覆盖抽象的空的handleError(IOException ioe),我们是在Reader接口中公开静态方法或构造器,接口使用Consumer 并将其应用于内部的通用ReaderImpl。

  你可以这样写

  Reader reader = Reader.builder()

  .withErrorHandler(IOException::printStackTrace)

  .build();

  而不要这样写

  Reader reader = new AbstractReader() {

  @Override

  public void handleError(IOException i

我的位置: 首页 >> Java 8 API 设计经验浅析

2018-09-22

来源:


 

在线答疑更多++

热门专题更多++

福建省福州市鼓楼区东大路恒裕大厦北大青鸟新生代校区

地址:福建省福州市鼓楼区东大路恒裕大厦三楼

电话:400-9966-370   0591-87880522

网址: www.0591bdqn.com

福州校区乘车路线:塔头站、东水路口站、八方大厦站

北大青鸟福州新生代校区公众平台

福州北大青鸟