到目前為止,已經負責API接近兩年了,這兩年中發現現有的API存在的問題越來越多,但很多API一旦釋出後就不再能修改了,即時升級和維護是必須的。一旦API發生變化,就可能對相關的呼叫者帶來巨大的代價,使用者需要排查所有呼叫的程式碼,需要調整所有與之相關的部分,這些工作對他們來說都是額外的。如果辛辛苦苦完成這些以後,還發現了相關的bug,那對使用者的打擊就更大。如果API經常發生變化,使用者就會失去對提供方失去信心,從而也會影響目前的業務。
但是我們為什麼還要修改API呢?為了API看起來更加漂亮?為了提供更多功能?為了提供更好的效能?還是僅僅覺得到了改變了時候了?對於使用者來說,他們更願意使用一個穩定但是看起來不那麼時髦的API,這並不意味著我們不再改進API了。當糟糕的API帶來的維護成本越來越大時,我想就是我們去重構它的時候。
如果可以回頭重新再做一遍,那麼我心目中的優秀的API應該是怎麼樣的?
判斷一個API是否優秀,並不是簡單地根據第一個版本給出判斷的,而是要看隨著時間的推移,該API是否還能存在,是否仍舊保持得不錯。槽糕的API介面各種各樣,但是好的API介面對於使用者來說必須滿足以下幾個點:
- 易學習:有完善的文件及提供儘可能多的示例和可copy-paste的程式碼,像其他設計工作一樣,你應該應用最小驚訝原則。
- 易使用:沒有複雜的程式、複雜的細節,易於學習;靈活的API允許按欄位排序、可自定義分頁、 排序和篩選等。一個完整的API意味著被期望的功能都包含在內。
- 難誤用:對詳細的錯誤提示,有些經驗的使用者可以直接使用API而不需要閱讀文件。
而對於開發人員來說,要求又是不一樣的:
- 易閱讀:程式碼的編寫只需要一次一次,但是當除錯或者修改的時候都需要對程式碼進行閱讀。
- 易開發:個最小化的介面是使用盡可能少的類以及儘可能少的類成員。這樣使得理解、記憶、除錯以及改變API更容易。
如何做到以上幾點,以下是一些總結:
1、 面向用例設計
如果一個API被廣泛使用了,那麼就不可能瞭解所有使用該API的使用者。如果設計者希望能夠設計出被廣泛使用的API,那麼必須站在使用者的角度來理解如何設計API庫,以及如何才能設計出這樣的API庫。
2、 採用良好的設計思路
在設計過程中,如果能按照下面的方式來進行設計,會讓這個API生命更長久
- 面向用例的設計,收集使用者建議,把自己模擬成使用者,保證API設計的易用和合理
- 保證後續的需求可以通過擴充套件的形式完成
- 第一版做盡量少的內容,由於新需求可以通過擴充套件的形式完成,因此儘量少做事情是抑制API設計錯誤的一個有效方案
- 對外提供清晰的API和文件規範,避免使用者錯誤的使用API,尤其是避免API(見第一節)靠後級別的API被使用者知曉與誤用
除此之外,下面還列出了一些具體的設計方法:
- 方法優於屬性
- 工廠方法優於建構函式
- 避免過多繼承
- 避免由於優化或者複用程式碼影響API
- 面向介面程式設計
- 擴充套件引數應當是便利的
- 對元件進行合理定位,確定暴露多少介面
- 提供擴充套件點
3、 避免極端的意見
在設計API的時候,一定要避免任何極端的意見,尤其是以下幾點:
- 必須漂亮(API不一定需要漂亮)
- API必須被正確地使用(使用者很難理解如何正確的使用API,API的設計者要充分考慮API被誤用的情況:如果一個API可能會被誤用,那麼它一定會被誤用)
- 必須簡單(我們總會面臨複雜的需求,能兩者兼顧的API是更好的API)
- 必須高效能(效能可以通過其他手段優化,不應該影響API的設計)
- 必須絕對相容(儘管本文一直提到如何保證相容,但是我們仍然要意識到,一些極少情況下會遇到的不相容是可以容忍的)
4、 有效的API評審
API設計完成以後,需要經過周密的設計評審,評審的重點如下:
- 用例驅動,評審前必須提供完善的使用用例,確保用例的合理性和完備性。
- 一致性,是否與系統中其他模組的介面風格一致,是否與對稱介面的設計一致。
- 簡單明瞭,API應該簡單好理解,容易學習和使用的API才不容易被誤用,給我們帶來更多的麻煩。
- API儘可能少,如果一個API可以暴露也可以不暴露,那麼就不要暴露他,等到使用者真正有需求的時候再將它成為一個公開介面也不遲。
- 支援持續改進,API是否能夠方便地通過擴充套件的方式增加功能和優化。
5、 提高API的可測試性
API需要是可測試的,測試不應依賴實現,測試充分的API,尤其是經過了嚴格的“相容性整合測試”的API,更能保證在升級的過程中不出現相容性問題。相容性整合測試,是指一組測試用例集合,這組測試用例會站在使用者的立場上使用API。在API升級以後,再檢測這組測試用例是否能完全符合預期的通過測試,儘可能的發現相容性問題。
6、 保證API的向後相容
對於每一個API的設計者來說,都渴望做到“向後相容”,因為不管是現在的API使用者,還是潛在的API使用者,都只信任那些可相容的API。但向後相容有多個層次上的意義,而且不同層次的向後相容,也意味著不同的重要性和複雜度。
7、 保持逐步改善
過去我們總希望能將現有的“不合理”的設計完全推翻,然後按照現在“美好”的思路,重新設計這個API,但是在一段時間以後,又會碰到一樣的狀況,需要再推翻一次。 如果我們沒有有效的逐步改善的辦法,依靠推翻現有設計,重新設計API只能讓我們回到起點,然後重現之前的過程。 要有一套行之有效的持續改善的辦法來在API相容的同時,改善API使之更好。
8、 把握API的生命週期
每一個API都是有生命週期的,我們需要讓API的生命週期更長,並且在API的生命週期結束時能讓其平滑的消亡。
- 告訴使用者我們是如何設計的,避免誤用,提供指導,錯誤的使用往往是縮短API壽命的一大殺手
- 提供試用期,API不可能一開始就是穩定,經過試用的API才能有更強的生命力
- 為API分級:內部使用;二次開發使用;開發或試用中;穩定;棄用API。避免API被濫用的同時,我們可以通過調整API的級別,來擴大其影響力,也能更優雅的結束一個API的生命週期。
開發API的過程其實就是一個溝通交流的過程。溝通的雙方就是API使用者和API設計者。
9、 一些具體的實施方案
在一個API不可避免要消亡或者改變的時候,我們應該接受並且面對這個事實,下面列舉了幾種保證相容性的前提下,對API進行調整的辦法:
- 將API標記為棄用,重新建立一個新的API。如果一個API不可避免要被消亡,這是唯一的辦法。
- 為其新增額外的引數或者引數選項來實現功能新增
- 將現有API拆成兩部分,提供一個精簡的核心API,過去的API通過封裝核心API上實現。這通常用於解決使用者需要一個程式碼精簡的版本時。
- 在現有的API基礎上進行封裝,提供一個功能更豐富的包或者類
一些好的API示例:
- Flickr API,這裡是文件的示例,同時提供了一個非常方便的API測試工具。
- Mediawiki API
- Ebay API,這裡有一個非常詳盡的文件示例。