一組好的API,必須要能夠讓客戶端程式碼在呼叫API中的函式時,明確知道API函式執行的結果。設計得宜的API函式,不僅可以讓客戶端程式碼區分執行結果究竟是成功或是失敗,同時也會讓客戶端程式碼在函式執行失敗時,有辦法知道發生錯誤的原因或者情況。

對於執行結果的相關訊息,需要更全盤掌握
這樣的資訊,對客戶端程式碼來說十分的重要。因為,並不是所有的API函式都能保證執行成功、不會發生任何錯誤。倘若無法讓客戶端程式碼得知執行的結果,那麼,客戶端程式碼就無法在執行發生異常時,進行相關的因應動作。

有些API設計者,僅利用API函式回傳一個代表成功或失敗的值(例如使用Boolean回傳型別),那麼,客戶端程式碼便僅能知道成功或失敗,卻無法更進一步準確找出失敗時的錯誤原因。一來,可能無法更適當地進行異常的處理動作,二來,客戶端程式設計者要進行除錯動作時,也會增加許多的難度。因此,提供更友善的API執行結果資訊,是一組好的API的必備條件。

有些API在錯誤資訊的提供上,是透過獨立的專門函式來完成。它們的API函式,執行時僅會回傳成功或失敗,但是讓客戶端程式碼在失敗時,另行呼叫特定函式來取得狀態的原因。例如,Windows的許多API函式在執行成功時,會回傳BOOL值,當回傳值為TRUE時,代表執行成功;為FALSE時,則代表執行失敗。而客戶端程式碼在得到API函式回傳FALSE時,可進一步呼叫GetLastError()這個函式,來取得失敗的錯誤代碼。

錯誤處理的典型做法
Windows的API總共定義了從0到15999的錯誤代碼,每個數字都有專門的意義。所以,在Windows的設計中,取得錯誤代碼的方式都一致,而所有錯誤代碼在每個可能回傳錯誤的API函式中,意義也都是一致的。當然,Windows也提供了將錯誤代碼對應至訊息字串的API函式FormatMessage,這有助於客戶端程式碼針對錯誤的情況,得以在日誌檔寫入時,或提示使用者訊息時,提供更有意義的訊息。Unix也有類似的設計,就像透過全域變數errno可以取得錯誤代碼、呼叫函式strerror()可以取得錯誤代碼對應的訊息。

至於對於採物件導向設計的API來說,採取異常(Exception)的機制來讓客戶端程式碼處理錯誤是十分直覺又理想的方式。不論採取那一種方式,API函式都應該為客戶端程式碼精確指出錯誤。

寫出API展示程式的重要性
當你完成一組API的設計及實作後,你最好撰寫一些相關的展示(demo)程式,用來展示你API所能發揮的功效。你可以假想若干個客戶端程式設計者最常會遭遇的情境,然後透過你的展示程式、運用你所設計的API來處理這些情境。

展示程式對想要嘗試使用API的程式設計者來說,用處很大。這些程式可以幫助他們更快理解:(1)運用你的API可以解決的重要問題,(2)究竟該如何運用你的API來解決問題,(3)運用你的API可以如何巧妙解決這些問題、省去他們多少力氣、讓他們的程式碼變得多優雅……等等。因此,好的展示程式碼比大量、冗長的文件更有幫助。

API的展示程式,除了可以向客戶端程式設計者傳達許多觀念之外,在API未對外釋出時,它也能讓API設計者,透過撰寫實際的程式,達到操演自身API的目的。

經過這樣的操演,能夠讓API的設計者,實際讓自己設身處地,發掘出設計API時會有的想法落差,同時了解究竟自己的API設計,是否需要其他的調校。

對API設計而言,一旦面對客戶端程式設計者的介面確立了,那麼,之後想要再做變動,所必須付出的代價就會相當大。因為,當你對客戶端程式設計者釋出後,他們便會基於你的設計開始撰寫客戶端程式碼,而日後API的修改,只要牽扯到介面的更動,所會波及到的程式碼其層面便很廣,所有的客戶端程式碼都有可能被影響到。

變更介面的時機與作為
當然,在設計階段便應該反覆推敲以驗證此事,但是,實際設想該寫些什麼樣的展示應用程式並且加以撰寫,便能夠透過實務來做最後的確認,以減少日後再度變更介面的可能。

既然談到了要降低變更介面的可能,就不能不談,倘若真的在釋出API提供給客戶端程式設計者開發他們的應用程式之後,發現你所設計的介面有所不足,甚至需要調整時,該怎麼辦?

修改原有的介面當然是下下策,既然API都釋出了,那麼便意謂著,基於你API所寫成的應用程式也有可能釋出,不然,也有可能正在撰寫中。修改原有的介面,等於是讓現有的程式碼作廢──除非客戶端程式碼並不打算升級到新版的API。

API難免遇到需要演化的情況,有時候是為了修正過去的錯誤、有時是為了調整,有時候是為擴充,以便加入更強大的功能。但,這些修正、調整及擴充,最好都能維持往下相容的特性,這樣才不會傷害到既有的客戶端程式碼。

我相信所有的客戶端程式設計者,都不會喜歡不維持往下相容的API,因為,客戶端程式設計者遇到這種情況,通常只有兩種選擇,其一是放棄升級到新版的API,而另一便是痛下決心,整個翻修。

放棄升級意謂著,只能一直維持現行版本,但若新版修正了重要錯誤,那麼不升級便無法獲得這些修正。翻修程式碼也是浩大的工程,需要投入的心力和時間也不小。因此,非到萬不得已、山窮水盡的局面,最好不要決定讓你的API往下不相容。

所有的改變,最好都透過擴充的方式去進行。但這考驗設計者的能耐。有一些微軟的API名稱後頭有著「Ex」兩個字母,而它們都有相對應沒有「Ex」兩個字的版本,例如CreateWindowEx()對應著CreateWindow()、RegisterClassEx()對應RegisterClass()。為什麼會有這一類的函式呢?因為都是設計者發現原來的版本不足以滿足需求、又不想改動原先版本的介面,所以只好擴充原有函式的介面,但以新函式的方式加入API之中。

雖然像這樣子的擴充方式看起來不是挺優雅的,起碼能夠達到目的,又能維持往下相容,也算是可接受的作法了。

此外,當你在釋出API之際,最好附上相關的單元測試碼。對於那些採取測試先行的設計者而言,這並不構成問題,因為他們在撰寫程式碼前,早就把單元測試用的程式碼給完成了。但,不管你是不是採測試先行的開發方式,能附上單元測試碼,對客戶端程式設計者來說,都能提供助益。他們可以確定你API的品質程度,也可以明白究竟他們所運用的API經過了那些測試。另一方面,測試程式往往也同時展示了運用API的方式,以及情境。

最後,所有的API釋出時,都應該附上足夠的文件說明。在這份文件中,最起碼要說明所有的API函式、每一個函式的作用、使用每個函式時的先決條件,當然,還有呼叫每個函式需要傳入那些參數、每個參數的意義、能接受的合法值範圍、回傳值的意義、以及可能會有那些執行結果的錯誤,如何得知究竟發生了什麼錯誤等等。

好的API設計影響深遠,好的API設計也能讓客戶端程式設計者時常感念在心。在本系列的文章中,試著介紹如何設計好的API,但這只是個起點,只要你能重視這件事,就會開始留意,不斷精進。

專欄作者

熱門新聞

Advertisement