Java接口的防御性API演进
API的發展絕對是不平凡的。 只有少數幾個需要處理的事情。 我們大多數人每天都在使用內部專有API。 現代IDE附帶了很棒的工具,可以分解,重命名,上拉,下推,間接,委托,推斷,泛化我們的代碼偽像。 這些工具使重構我們的內部API變得輕而易舉。 但是我們中的一些人在公共API上工作,規則在此方面發生了巨大變化。 如果正確完成,則對公共API進行版本控制。 每次更改(兼容或不兼容)都應在新的API版本中發布。 多數人會同意,API升級應在主要版本和次要版本中完成,類??似于語義版本控制中指定的內容 。 簡而言之:不兼容的API更改發布在主要版本(1.0、2.0、3.0)中,而兼容的API更改/增強發布在次要版本(1.0、1.1、1.2)中。
如果您正在計劃,那么您將在很長時間內預見到大多數不兼容的更改,然后才實際發布下一個主要版本。 Java中一個早日宣布這種變化的好工具是棄用 。
接口API的演變
現在,棄用是一個很好的工具,它表明您將要從API中刪除類型或成員。 如果要在接口的類型層次結構中添加方法或類型怎么辦? 這意味著實現您的接口的所有客戶端代碼都將中斷–至少只要尚未引入Java 8的防御方法即可。 有幾種技術可以規避/解決此問題:
1.不在乎
是的,這也是一種選擇。 您的API是公開的,但使用的程度可能不是很高。 讓我們面對現實:并不是我們所有人都在JDK / Eclipse / Apache / etc等代碼庫上工作。 如果您很友好,則至少要等待主要版本引入新方法。 但是,如果確實需要,您可以打破語義版本控制的規則-如果您可以處理引起一群憤怒的用戶的后果。
但是請注意,其他平臺并不像Java Universe那樣向后兼容(通常是根據語言設計或語言復雜性)。 例如,使用Scala將事物聲明為隱式的各種方法,您的API并不總是完美的。
2.用Java方式完成
“ Java”方式根本不發展接口。 JDK中的大多數API類型一直以來都是今天。 當然,這使API感覺很“恐龍化”,并在各種相似類型之間(例如StringBuffer和StringBuilder或Hashtable和HashMap)增加了很多冗余。
請注意,Java的某些部分不遵循“ Java”方式。 最具體地說,JDBC API就是這種情況,它根據第1節“不關心它”的規則演變。
3.用Eclipse的方式來做
Eclipse的內部包含大量API。 在Eclipse中進行開發時, 有很多指導方針如何開發自己的API(即,插件的公共部分)。 關于Eclipse人員如何擴展接口的一個示例是IAnnotationHover類型。 根據Javadoc合同,它允許實現也實現IAnnotationHoverExtension和IAnnotationHoverExtension2 。 顯然,從長遠來看,這種經過改進的API很難維護,測試和記錄文檔,最終很難使用! (考慮ICompletionProposal及其6(!)擴展類型)
4.等待Java 8
在Java 8中,您將能夠使用防御者方法 。 這意味著您可以為新的接口方法提供明智的默認實現 ,如Java 1.8的java.util.Iterator (摘錄)所示:
public interface Iterator<E> {// These methods are kept the same:boolean hasNext();E next();// This method is now made 'optional' (finally!)public default void remove() {throw new UnsupportedOperationException('remove');}// This method has been added compatibly in Java 1.8default void forEach(Consumer<? super E> consumer) {Objects.requireNonNull(consumer);while (hasNext())consumer.accept(next());} }當然,您并不總是希望提供默認的實現。 通常,您的界面是必須完全由客戶端代碼實現的合同。
5.提供公共默認實現
在許多情況下,明智的做法是告訴客戶端代碼,他們可能需要自己承擔實現接口的風險(由于API的發展),因此,他們應該更好地擴展提供的抽象或默認實現。 一個很好的例子是java.util.List ,可能很難正確實現。 對于簡單的,對性能不重要的自定義列表,大多數用戶可能選擇擴展java.util.AbstractList 。 然后剩下剩下要實現的唯一方法是get(int)和size()。所有其他方法的行為都可以從這兩個方法中得出:
class EmptyList<E> extends AbstractList<E> {@Overridepublic E get(int index) {throw new IndexOutOfBoundsException('No elements here');}@Overridepublic int size() {return 0;} }遵循的一個很好的約定是,如果您的默認實現為AbstractXXX,則將其命名為默認實現;如果是具體的,則將其命名為DefaultXXX
6.使您的API很難實現
現在,這并不是真正的好技術,而只是一個可能的事實。 如果您的API很難實現(一個接口中有100多個方法),則用戶可能不會這樣做。 注意: 可能 。 永遠不要低估瘋狂的用戶。 一個示例是jOOQ的 org.jooq.Field類型,它表示數據庫字段/列。 實際上,這種類型是jOOQ 內部域特定語言的一部分 ,提供了可以在數據庫列上執行的各種操作和功能。 當然,擁有太多方法是一個例外,并且-如果您不設計DSL-可能表明整體設計不佳。
7.添加編譯器和IDE技巧
最后但并非最不重要的一點是,您可以將一些巧妙的技巧應用于您的API,以幫助人們了解他們應該做些什么,以便正確實現基于接口的API。 這是一個艱難的例子,將API設計人員的意圖直接撲向您的臉。 考慮一下org.hamcrest.Matcher API的摘錄:
public interface Matcher<T> extends SelfDescribing {// This is what a Matcher really does.boolean matches(Object item);void describeMismatch(Object item, Description mismatchDescription);// Now check out this method here:/*** This method simply acts a friendly reminder not to implement * Matcher directly and instead extend BaseMatcher. It's easy to * ignore JavaDoc, but a bit harder to ignore compile errors .** @see Matcher for reasons why.* @see BaseMatcher* @deprecated to make*/@Deprecatedvoid _dont_implement_Matcher___instead_extend_BaseMatcher_(); }“友好的提醒” ,來吧。
其他方法
我敢肯定,還有許多其他方法可以開發基于接口的API。 我很好奇您的想法!
參考: JAVA,SQL和ANDOQ博客上的JCG合作伙伴 Lukas Eder 提供了Java接口的防御性API演變 。
翻譯自: https://www.javacodegeeks.com/2013/02/defensive-api-evolution-with-java-interfaces.html
總結
以上是生活随笔為你收集整理的Java接口的防御性API演进的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如果微信理财通里的钱没有了怎么办?
- 下一篇: 深圳新房备案价查询官网(深圳新房备案价查