在前一回中,我們提到,一組API所提供的功能應該要恰到好處,若是提供的功能太少,那麼客戶端程式設計者需要自己做的事就多,那麼API的價值就變低。但同樣的,功能包山包海的API不見得是好事,因為這麼一來,雖然有可能提供更多的作用、更好的彈性,但是相對而言,複雜度也會提高,客戶端程式設計者在學習時要花更多時間才能理解、也要花更多時間才能上手。同時,愈複雜的API使用上愈容易犯錯。

如何考量該由API負責的事情
在我們的經驗中,我們都使用過一些其實十分強大的API,但是,有時花在學習、花在錯誤排除的時間,還多過於API能為我們省下的時間。我們一方面期待API涵蓋的功能最小化、一方面又期待API所提供的功能夠完整,這兩個目標當然是相互衝突的。而設計者的工作,便是在兩種情況間取得一個良好的平衡。

API設計者和客戶端程式設計者是分工合作的,但二者之間應該依照什麼樣的原則來分工,那些東西適合畫歸API、又有那些適合留給客戶端程式碼完成呢?

當你在設計一組API時,通常會設定一個應用的領域,進而設想客戶端程式設計者在這個應用領域中可能的應用情境。你可以想像,倘若缺少了這組API時,程式設計者必須自行寫下那些程式碼,才能完成你所設想的諸般應用情境。接著,你便可以針對這些應用情境進行分析,藉以分出API與客戶端程式碼的界線。

有幾個基本的畫分原則可以參考,你可以視分析程式碼所完成之工作:(1)低階高階與否(2)通用與專用與否(3)常用與罕用與否,來進行畫分的依據。

就像套用成語一樣方便,用更簡易的方式駕馭複雜的底層功能
你可以很輕易區分出,那些工作是低階、那些是高階。低階畫給API,高階則留給客戶端程式碼。API之所以存在的首要目的,便是將程式實作時繁瑣的低階細節予以抽離並包裝,成為抽象化、可重複使用的程式碼,使得基於此API的客戶端程式設計者,毋需在一次又一次的開發中,反覆地重新實作相同的程式碼。

若你觀察C的檔案處理程式庫,便會發現,它成功地界定程式設計者在處理檔案時,API和客戶端程式碼間的界線。

如果我們沒有一套檔案處理API,我們自己必須實作那些事呢?舉例來說,得自己想辦法記錄每個檔案的名稱、每個檔案的內容究竟位在那個磁頭、磁軌、磁區,然後當我們想讀取某個檔案時,才能藉以找到這個檔案的資料,究竟是位於那些磁區之上,然後應該依照怎樣的讀取順序,才能依序、或是隨機地讀出檔案資料。同樣的,寫入資料的情況也差不多。

當然,我還可以舉出更多繁瑣的低階細節,像是檔案讀取寫入時的緩衝機制,以提升檔案處理時的效率等等,但這並不是這邊打算討論的重點。

重點是,檔案處理程式庫的設計者決定了究竟那些細節應該由API這邊畫出一道明確的界線,使得這些細節被阻隔在應用程式設計者之外。你看到的像是fread()、fwrite(),只要透過它們,便可以輕易進行檔案的讀取及寫入。你可能還需要像是fseek()來幫助你設定要讀取檔案的位置。有了這樣的API,你再也不需要明白究竟檔案的處理實際上會涉及那些細節,當然,你更不需要自己實作這些細節。

你可以想像,倘若這些檔案處理最低階的部份,都需要應用程式設計者自行實作的話,這樣子的軟體開發世界究竟有多可怕。但有了這些API函式後,事情簡化到很高的程度。

這些低階的細節,都是每個應用程式設計者在處理檔案時都會面臨的,所以由API以下的層次加以包裝起來之後,應用程式設計者,自然可以省去大量的時間。事實上,API的責任在於定義界線,就檔案處理的這個例子來說,有更多工作並不是應用程式庫完成的。

除了省去客戶端程式設計者的開發時間之外,你可以看到將低階細節隱藏起來的另一個威力所在,便是為客戶端程式設計者提供一個更高階、更簡潔、更直觀、更接近應用領域的程式設計模型。

因此,客戶端程式設計者在撰寫程式時,心裡想的像是:「我現在要從某個檔案讀取1024個位元組的動作」,而不是,「我要找出某個檔案究竟位在那些磁區上,然後我要讀取的檔案位置究竟位在那個磁區中,在讀取之前,我得先看看程式裡的緩衝區裡是否有想讀取的資料,有的話,就直接從緩衝區裡讀取,反之,則移動硬碟的讀寫頭到指定的磁區上,然後讀取1024個位元組」。

立足於API之上的高階程式設計,有點像是運用成語一樣,可以讓寫作的人,表達出同樣的意思,但更精要、有時甚至還更為傳神。我們從高階低階的角度來畫分API和客戶端程式碼之間的分工責任時,除了思考能為客戶端程式設計者省去多少重複的工作之外,更值得思考:客戶端在運用API時,程式碼所呈現出來的面貌究竟會是如何。

如果透過你所設計的API,所編寫出來的程式碼,變得更加的直覺、更容易讀懂,那麼代表你的設計是成功的。反之,你就需要檢討為何自己所設計的API,反而讓客戶端程式設計者所寫下的程式碼變得不直覺、甚至可讀性降低。

用八十/二十法則來考量,通用的部份交給API來處理
除了高階、低階這個畫分的原則之外,通用與專用則是第二個原則。何謂「通用」?通用就是指在你所設定的應用領域中,客戶端程式設計者廣泛都會需要用到的。相反的,所謂的專用,便是在這個應用領域中,僅有少數特定需求的客戶端程式設計者才會需要用到。

「廣泛」、「少數」,都是不那麼精確的詞,這意謂著,當你依據這個標準在進行判斷時,必須針對各種條件來拿捏其中的分寸。

正如前面提到的,我們會希望在最小化API的前提下,盡可能地滿足最多客戶端程式設計者。特定的需求會被最小化的前提給過濾掉。這邊或許可以基於八十/二十法則來做決定,用20%的功能,來滿足80%的應用需求。

我在這裡建議的第三個原則是,將常用的部份畫歸到API,將罕用的留給客戶端程式碼。同樣的,這也是因為最小化API的前提之下必須要有的取捨。

有一些API的設計者,固然考量到在所設定的應用領域中,需要被滿足的常用需求,但他們的企圖心太大,他們還把那些冷門、根本不太會有客戶端程式設計者需求的功能,也都納入API的範圍之內。為了幾乎不太會被用到的功能,而讓API變大、變複雜,事實上,是十分不划算的一件事。

如果不將那些罕用的功能放到API裡,即使在少數的情況下,客戶端程式設計者會需要使用,因為情況終究是少數,所以,那時再讓客戶端程式設計者自行實作即可。

API的設計者還是必須從顧及全貌的立場出發來考量自己的設計。在「常用」及「罕用」的取捨上,一樣可以利用八十/二十法則來做為衡量的標準。

決定API的範圍和內容,是開發人員設計API時很關鍵的一件事,在本文中提供了幾種方式供你參考。在下一回中,我們會進一步討論和API設計有關的其他議題。

專欄作者


熱門新聞

Advertisement