目錄
不要用回傳Null來表示一個空值
不要將陣列作為API的傳入參數和傳回值
考虑添加静态接口方法以提供用于对象创建的单个入口点
青睐功能性接口和Lambdas的组合优于继承
确保你将@FunctionalInterface注解添加到功能性接口
避免使用功能性接口作为参数的重载方法
避免在接口中过度使用默认方法
确保在执行之前进行API方法的参数不变量检查
不要简单地调用Optional.get()
考虑在不同的API实现类中分行调用接口
首頁 Java java教程 Java 8 API 設計經驗淺析

Java 8 API 設計經驗淺析

Feb 23, 2017 am 10:46 AM
api java8

本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃!

任何寫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<String> 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<String> 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 ioe) {
        ioe. printStackTrace();
    }
};
登入後複製


确保你将@FunctionalInterface注解添加到功能性接口

使用@FunctionalInterface注解标记的接口,表示API用户可以使用lambda实现接口,并且还可以通过防止抽象方法随后被意外添加到API中来确保接口对于lambdas保持长期使用。

你可以这样写

@FunctionalInterface
public interface CircleSegmentConstructor {
    CircleSegment apply(Point cntr, Point p, double ang);
    // abstract methods cannot be added
}
登入後複製


而不要这样写

public interface CircleSegmentConstructor {
    CircleSegment apply(Point cntr, Point p, double ang);
    // abstract methods may be accidently added later
}
登入後複製


避免使用功能性接口作为参数的重载方法

如果有两个或更多的具有相同名称的函数将功能性接口作为参数,那么这可能会在客户端侧导致lambda模糊。例如,如果有两个Point方法add(Function renderer) 和add(Predicate logCondition),并且我们尝试从客户端代码调用point.add(p -> p + “ lambda”) ,那么编译器会无法确定使用哪个方法,并产生错误。相反,请根据具体用途考虑命名方法。

你可以这样写

public interface Point {
    addRenderer(Function<Point, String> renderer);
    addLogCondition(Predicate<Point> logCondition);
}
登入後複製


而不要这样写

public interface Point {
    add(Function<Point, String> renderer);
    add(Predicate<Point> logCondition);
}
登入後複製


避免在接口中过度使用默认方法

默认方法可以很容易地添加到接口,有时这是有意义的。例如,想要一个对于任何实现类都期望是相同的并且在功能上要又短又“基本”的方法,那么一个可行的候选项就是默认实现。此外,当扩展API时,出于向后兼容性的原因,提供默认接口方法有时是有意义的。

众所周知,功能性接口只包含一个抽象方法,因此当必须添加其他方法时,默认方法提供了一个安全舱口。然而,通过用不必要的实现问题来污染API接口以避免API接口演变为实现类。如果有疑问,请考虑将方法逻辑移动到单独的实用程序类和/或将其放置在实现类中。

你可以这样写

public interface Line {
    Point start();
    Point end();
    int length();
}
登入後複製


而不要这样写

public interface Line {
    Point start();
    Point end();
    default int length() {
        int deltaX = start().x() - end().x();
        int deltaY = start().y() - end().y();
    return (int) Math.sqrt(
        deltaX * deltaX + deltaY * deltaY
        );
    }
}
登入後複製


确保在执行之前进行API方法的参数不变量检查

在历史上,人们一直草率地在确保验证方法输入参数。因此,当稍后发生结果错误时,真正的原因变得模糊并隐藏在堆栈跟踪下。确保在实现类中使用参数之前检查参数的空值和任何有效的范围约束或前提条件。不要因性能原因而跳过参数检查的诱惑。

JVM能够优化掉冗余检查并产生高效的代码。好好利用Objects.requireNonNull()方法。参数检查也是实施API约定的一个重要方法。如果不想API接受null但是却做了,用户会感到困惑。

你可以这样写

public void addToSegment(Segment segment, Point point) {
    Objects.requireNonNull(segment);
    Objects.requireNonNull(point);
    segment.add(point);
}
登入後複製


而不要这样写

public void addToSegment(Segment segment, Point point) {
    segment.add(point);
}
登入後複製


不要简单地调用Optional.get()

Java 8的API设计师犯了一个错误,在他们选择名称Optional.get()的时候,其实应该被命名为Optional.getOrThrow()或类似的东西。调用get()而没有检查一个值是否与Optional.isPresent()方法同在是一个非常常见的错误,这个错误完全否定了Optional原本承诺的null消除功能。考虑在API的实现类中使用任一Optional的其他方法,如map(),flatMap()或ifPresent(),或者确保在调用get()之前调用isPresent()。

你可以这样写

Optional<String> comment = // some Optional value 
String guiText = comment
  .map(c -> "Comment: " + c)
  .orElse("");
登入後複製


而不要这样写

Optional<String> comment = // some Optional value 
String guiText = "Comment: " + comment.get();
登入後複製


考虑在不同的API实现类中分行调用接口

最后,所有API都将包含错误。当接收来自于API用户的堆栈跟踪时,如果将不同的接口分割为不同的行,相比于在单行上表达更为简洁,而且确定错误的实际原因通常更容易。此外,代码可读性将提高。

你可以这样写

Stream.of("this", "is", "secret") 
  .map(toGreek()) 
  .map(encrypt()) 
  .collect(joining(" "));
登入後複製


而不要这样写

Stream.of("this", "is", "secret").map(toGreek()).map(encrypt()).collect(joining(" "));
登入後複製

           

 以上就是Java 8 API 设计经验浅析 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1318
25
PHP教程
1268
29
C# 教程
1248
24
PHP和Manticore Search開發指南:快速建立搜尋API PHP和Manticore Search開發指南:快速建立搜尋API Aug 07, 2023 pm 06:05 PM

PHP和ManticoreSearch開發指南:快速建立搜尋API搜尋是現代Web應用程式中不可或缺的功能之一。無論是電子商務網站、社交媒體平台還是新聞門戶,都需要提供一個高效、準確的搜尋功能來幫助用戶找到他們感興趣的內容。而ManticoreSearch作為一個效能卓越的全文搜尋引擎,為我們提供了一個強大的工具來創建出色的搜尋API。本文將向您介紹如何

如何在PHP專案中透過呼叫API介面來實現資料的爬取與處理? 如何在PHP專案中透過呼叫API介面來實現資料的爬取與處理? Sep 05, 2023 am 08:41 AM

如何在PHP專案中透過呼叫API介面來實現資料的爬取與處理?一、介紹在PHP專案中,我們經常需要爬取其他網站的數據,並對這些數據進行處理。而許多網站提供了API接口,我們可以透過呼叫這些接口來取得資料。本文將介紹如何使用PHP來呼叫API接口,實現資料的爬取與處理。二、取得API介面的URL和參數在開始之前,我們需要先取得目標API介面的URL以及所需的

Oracle API使用指南:探索資料介面技術 Oracle API使用指南:探索資料介面技術 Mar 07, 2024 am 11:12 AM

Oracle是一家全球知名的資料庫管理系統供應商,其API(ApplicationProgrammingInterface,應用程式介面)是一種強大的工具,可協助開發人員輕鬆地與Oracle資料庫互動和整合。在本文中,我們將深入探討OracleAPI的使用指南,向讀者展示如何在開發過程中利用資料介面技術,同時提供具體的程式碼範例。 1.Oracle

Oracle API整合策略解析:實現系統間無縫通信 Oracle API整合策略解析:實現系統間無縫通信 Mar 07, 2024 pm 10:09 PM

OracleAPI整合策略解析:實現系統間無縫通信,需要具體程式碼範例在當今數位化時代,企業內部系統之間需要相互通信和資料共享,而OracleAPI就是幫助實現系統間無縫通信的重要工具之一。本文將從OracleAPI的基本概念和原則入手,探討API整合的策略,最終給出具體的程式碼範例幫助讀者更好地理解和應用OracleAPI。一、OracleAPI基本

React API呼叫指南:如何與後端API互動和資料傳輸 React API呼叫指南:如何與後端API互動和資料傳輸 Sep 26, 2023 am 10:19 AM

ReactAPI呼叫指南:如何與後端API進行互動和資料傳輸概述:在現代的Web開發中,與後端API進行互動和資料傳輸是一個常見的需求。 React作為一個流行的前端框架,提供了一些強大的工具和功能來簡化這個過程。本文將介紹如何使用React來呼叫後端API,包括基本的GET和POST請求,並提供具體的程式碼範例。安裝所需的依賴:首先,確保在專案中安裝了Axi

開發建議:如何利用ThinkPHP框架進行API開發 開發建議:如何利用ThinkPHP框架進行API開發 Nov 22, 2023 pm 05:18 PM

開發建議:如何利用ThinkPHP框架進行API開發隨著網際網路的不斷發展,API(ApplicationProgrammingInterface)的重要性也日益凸顯。 API是不同應用程式之間進行通訊的橋樑,它可以實現資料共享、功能呼叫等操作,為開發者提供了相對簡單且快速的開發方式。而ThinkPHP框架作為一款優秀的PHP開發框架,具有高效能、可擴展且易用

如何處理Laravel API報錯問題 如何處理Laravel API報錯問題 Mar 06, 2024 pm 05:18 PM

標題:如何處理LaravelAPI報錯問題,需要具體程式碼範例在進行Laravel開發時,常會遇到API報錯的情況。這些報錯可​​能來自於程式碼邏輯錯誤、資料庫查詢問題或是外部API請求失敗等多種原因。如何處理這些報錯是一個關鍵的問題,本文將透過具體的程式碼範例來示範如何有效處理LaravelAPI報錯問題。 1.錯誤處理在Laravel

使用Python將API資料儲存為CSV格式 使用Python將API資料儲存為CSV格式 Aug 31, 2023 pm 09:09 PM

在資料驅動的應用程式和分析領域,API(應用程式介面)在從各種來源檢索資料方面發揮著至關重要的作用。使用API​​資料時,通常需要以易於存取和操作的格式儲存資料。其中一種格式是CSV(逗號分隔值),它允許有效地組織和儲存表格資料。本文將探討使用強大的程式語言Python將API資料儲存為CSV格式的過程。透過遵循本指南中概述的步驟,我們將學習如何從API中檢索資料、提取相關資訊並將其儲存在CSV檔案中以供進一步分析和處理。讓我們深入了解使用Python進行API資料處理的世界,並釋放CSV格式的潛

See all articles