先把 Joshua Bloch 大神的 API PDF 放在這裏膜拜下:「How to Design a Good API and Why it Matters.pdf」
前端
在設計和實現通用訂單搜索API的過程當中,收穫了一點關於API設計的得與失。總結下,但願能給後面的工做帶來有益的幫助。
java
什麼是好的API ?框架
簡潔、清晰、易懂、易使用。 語義行爲與選項分離。
ui
得:搜索引擎
從交易訂單搜索能力化角度思考API,充分考慮了組合性,作到足夠靈活,同時兼顧可讀性。可以在較少改動和發佈 trade-manage 的基礎上,實現多樣化的需求。
設計
失:code
在過於注重靈活性功能的狀況下,有一些地方作的不夠好,給使用方帶來不少困擾。對象
本文主要討論其得與失,但願可以給後來者帶來有益的啓發。
blog
強調從能力的角度而不是業務場景角度來思考API設計,考慮足夠靈活性。 好比繼承
定義交易訂單搜索能力須要的參數,而不是依賴頁面傳過來的參數。經過定義的參數來組合搜索能力。
雖然搜索頁面只須要傳一個訂單類型,可是後臺容許傳多個訂單類型,爲後續擴展性留下空間。
基於自身定義的訂單搜索參數,後臺實現也很是簡潔,而不須要作各類參數的解析和適配。對於搜索能力的穩定性和減小BUG的發生,是很是有益的。
一體兩面,總有正面和反面的地方。因爲通用訂單搜索API 特別強調基礎層的能力化和穩定性,在基礎層與業務層之間缺少一道友好的適配層,給業務方使用也帶來了很多曲折。
其實通用訂單搜索API 的文檔也作了很多工做。 接口入參 ,代碼示例, 搜索入參的分類,包括代碼裏 java doc 其實也寫得很清楚。 爲何業務方還來諮詢呢? 一個緣由,確實 API 設計有失當之處(考慮了最主要的PC列表頁面搜索場景),對於業務方的簡單需求(好比按照下單人ID)顯得過於複雜了; 另外一個緣由,我認爲開發人員對於陌生事物仍是缺少必要的耐心。固然我其實也有這個毛病。 若是說文檔沒有,代碼裏 java doc API 也缺失,過來諮詢是沒問題的,可是文檔 ,javadoc 註釋寫的很明白了,花2分鐘稍微掃一下,可以學到更多東西,就不會爲了一個小需求來諮詢了。
可是文檔確實也有寫的不夠的地方。
好比返回的錯誤信息,文檔沒有寫明。因而聯調的時候,業務方遇到「非法的調用源」,一臉懵逼。須要有一些錯誤提示文檔 ; 好比業務方遇到其餘的錯誤,也不知道是什麼緣由,只能找咱們來查。
好比沒有突出經常使用的須要的東西。每每業務方內部依賴訂單搜索列表的時候,基於應用場景,可能更多依賴於 訂單狀態,訂單類型,粉絲ID,下單人ID,維權狀態, 這些能夠單獨抽離一個更 brief 的訂單搜索接口,並輔以文檔,也許就不會遭遇不知道用哪一個參數的問題了。 換句話說, 原來過於依賴能力化的角度思考接口,缺失的一塊是, 基於應用場景的角度來思考接口。
先說下背景。當時是由於老搜索接口不太靈活,且參數與前端頁面耦合比較緊,所以決定設計一個新的通用訂單搜索接口,以提供更加靈活的搜索能力。
第一個失,就是不加思索地把老搜索接口裏的參數所有拷貝過來。 好比說 毫無卵用的 訂單標記,被廢棄還讓業務方傳錯的 pageSize, pageNo 。
經驗: 因爲新接口上線和接入一般有一個過程,其實沒必要要當即支持全部可能的搜索請求,而是先支持最經常使用的部分,而後根據狀況擴展能力。 API 參數一般是越嚴格越少更好,公開了就收不回來了。
設計新接口入參時,能夠參考老接口入參,但必定要慎之又慎,只取必需的,嚴格把控每一個參數的必要性。
參數必定要僅僅針對接口服務而言,不要爲了圖方便把全部入參都混雜到一個類裏,致使API語義不清,給業務方使用帶來困惑。
第二個失,過於追求複用代碼和語義分離,繼承層次比較深。
追求代碼複用是好習慣,但不能過分。尤爲 API 設計,應該以「清晰易使用」爲重。 以下是個反例:
經驗: 繼承通常是爲了實現不一樣的語義分離,好比基礎通用參數與業務參數, 但強烈建議最多兩層,繼承層次毫不要超過兩層。
public class PaginationParam extends BaseParam { } public class GeneralOrderSearchParam extends PaginationParam { protected String index = "trade_es_index"; /** * 訂單查詢對象 */ protected OrderSearchParam orderSearchParam; // ... } public class OrderSearchParam extends BaseParam { }
第三個失,沒有充分考慮業務方的使用體驗。
層次感不強
全部的搜索參數都平鋪到一個 OrderSearchParam 的字段裏,沒有層次感, 業務方每每只要根據一個字段搜索,卻要在繁多的搜索字段裏搜索。
構造入參不方便
缺少方便的構造搜索入參的方法,業務方須要寫很無聊的 set , set , set 來構造搜索入參,代碼會比較難看。 能夠考慮使用 Builder 模式來構造經常使用參數。這個方法也能夠解決 層次感不強 的問題。
經驗: 應該對搜索字段進行區分對待,重點突出, 能夠將最經常使用的搜索字段聚合成一個子搜索對象,有關聯語義的搜索字段聚合成一個子搜索對象。 讓業務方可以快速找到所須要的。經過提供易懂的 API 示例,也能夠幫助業務方更好地使用 API。
經常使用搜索重複構建
因爲設計參數時,參數取值過於原子化,強調靈活性與可組合性,須要業務方根據搜索場景自行組裝參數,當多個業務方來對接某個經常使用搜索場景時,每一個業務方都須要寫相同的代碼。
if (someCond) { setOrderTypeDesc(Arrays.asList(xEnum.name(),yEnum.name(),zEnum.name())) }
若是後續設計有變動,每一個業務方都必須修改一遍。所以,更好的作法是,若組合語義佔了經常使用80%的情形,那麼應該清晰定義這些組合語義,內部作映射消化掉,而不是委託給外部。
爲了圖方便,直接在原來的參數里加字段,而不是創造新的參數對象,致使原有的參數對象混雜不一樣的功能,給業務方使用帶來困惑。
好比原來有個實時詳情接口的入參 OrderQueryOptions , 非實時詳情接口,多了個 withDeliveryInfo 可選參數,雖然放在原來的參數對象會省很多事,但是會讓接口語義不清。對應實時接口來講,它根本不須要這個參數;對於非實時接口,又混雜了實時接口的入參。 兩邊都不討好。
經驗: 嚴格對接口入參進行控制,與接口服務無關的參數不容許加入。實現細節相關的東西不加入API,不公開。
爲了具備更好的可擴展能力,減小發布,設計了一個入參 extendKeywords 。當要搜索的字段不在給定搜索入參時,能夠直接指定 ES 裏對應的字段及操做符來搜索。 雖然靈活了些,卻將業務方與底層實現牢牢綁在一塊兒。若是之後要改用其餘的搜索引擎,這種就是很是難以擺脫的困擾了。對於API設計與實現來講,都是很差的決策。
API 是服務提供的接口抽象。 一旦公開,收回來會很是困難並且費力。所以,設計API 要精思細慮,嚴格把關每一個入參字段、繼承層次不要超過兩次、充分考慮使用者體驗、避免參數混雜、避免暴漏實現細節等問題。
API 好書推薦:《軟件框架設計的藝術》,英文書名是:《 Practical API Design: Confessions of a Java Framework Architect 》