設計一組API時,我們所關心的,不單只是自己的程式碼該設計成什麼樣子,我們同樣關心,使用這組API的客戶端程式設計者,他們的程式碼應該要呈現出什麼面貌。我們所考量的,不僅僅是自己該如何寫下程式碼,更重要的是,我們還得設想,客戶端程式設計者「將會」如何寫下他們的程式碼,因為API的形態也會影響到使用API之程式碼的樣貌。倘若基於設計醜陋的API,想寫出優雅的程式碼,恐怕十分困難。

提升程式碼的可讀性
所以,所有我們在撰寫程式碼時,希望程式碼具備的良好特質,我們也都希望基於我們API而寫下的程式碼,也一樣能夠擁有。程式碼該有什麼好特質呢?例如,「易讀」、「易擴充」、「單純」、「容易維護」、以及「高效率」。而在這些特質之中,讓客戶端程式碼容易閱讀無疑是API設計時的首要關鍵。好的API能導出可讀性高的程式碼,當然,不好的API也能輕易製造出不容易閱讀的程式碼。

就拿Windows的一條API函式來看吧!以下的程式碼片段,取自於MSDN對使用Window Classes的例子:

hwnd = CreateWindow(
"MainWClass", // name of window class
"Sample", // title-bar string
WS_OVERLAPPEDWINDOW, // top-level window
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
CW_USEDEFAULT, // default width
CW_USEDEFAULT, // default height
(HWND) NULL, // no owner window
(HMENU) NULL, // use class menu
hinstance, // handle to application instance
(LPVOID) NULL); // no window-creation data

CreateWindow這個函式是用來建立視窗用的,它必須傳入11個參數(太多參數也是降低可讀性的原因之一),但倘若沒有逐一的註解,任你是如何天縱英明的程式設計者,也很難記住這11個參數究竟分別是什麼作用,更別提剛接觸程式碼的閱讀者,在不查閱API說明文件的前提下,能搞懂撰寫者究竟在建立視窗時傳入各個參數的實際意義。例如,讀者看到連續四個CW_USEDEFAULT(第4個參數至第7個參數)時,又有幾人能夠輕易參透,第6個USEDEFAULT意指視窗高度為系統預設高度呢?

這便是這個API函式的問題所在──這API函式本身的設計,使得運用這API函式的客戶程式碼可讀性會降低。就功用來說,它有沒有達到它應有的作用?有!它有。但是,它卻使得程式設計者無論如何都很難寫出有可讀性的客戶端程式碼。倘若程式碼中沒有為每個傳入的參數旁邊加上註解,實在很難理解每個參數究竟具有什麼意義。每個這就是我所說的「API的形態也會影響到使用API之程式碼的樣貌」,而這個例子,正說明了API設計的不夠好,的確會構成客戶端程式碼撰寫時的先天障礙。

倘若,這API稍微修改一下,我們先設計一個結構來存放欲設定的視窗特性,像是:

struct WindowProperty
{
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
};

然後修改API的介面,一來降低傳入的參數個數,二來我們還得增加客戶端程式碼撰寫時的可讀性。倘若運用增加的WindowProperties,客戶端程式碼就可以寫作如下的樣子:

WindowProperties wp;
wp.dwStyle = WS_OVERLAPPEDWINDOW;
wp.x = CW_USEDEFAULT;
wp.y = CW_USEDEFAULT;
wp.nWidth = CW_USEDEFAULT;
wp.nHeight = CW_USEDEFAULT;
wp.hWndParent = (HWND) NULL;
wp.hMenu = (HMENU) NULL;
hwnd = CreateWindow("MainWClass", “Sample”, wp, hinstance, (LPVOID) NULL);

撰寫程式時可能要多打點字──不過,利用有自動補齊(auto-completion)的IDE來撰寫,其實不需要多打什麼字,但透過引入了一個結構,客戶端程式碼在撰寫時,因為需要逐一指定結構的欄位名稱並指派其值,所以,讀者反而可以透過指派值時的欄位名稱,來明白每個參數的意義。一來參數變少了,二來可讀性大大的提高了。

我們很難追溯這個API函式如此設計的原意,但它的確衍生出可讀性的問題。在Windows API中,有些需要眾多參數的函式,也運用了另行制定的結構來儲放傳入參數的技巧,例如RegisterClassEx(),這代表著其API設計也充滿了一些不同的風格。

從用API的人的角度,來思考程式碼可讀性
從這個修改的範例來看,API函式設計的良窳,構成了客戶端程式碼可讀性的先天條件。當你在設計API時,將自己切換到客戶端程式設計者的觀點是很重要的一個動作。客戶端程式設計者其實就是你的客戶、你的使用者。有些API設計者只考慮到自己的便利,的確實作出來的功能都能達到相同的目的,卻是挑選對自己方便的設計方式,但有時候這對客戶端程式設計者而言,卻不一定同樣方便。

考慮客戶端程式碼的可讀性,便是設計時站在客戶端程式設計者的立場,來檢視自己所做出來的結果。此外,所有設計也都應該基於這個原則。例如,要考慮客戶端程式設計者在運用API時,因為對API行為了解不夠深入而發生誤用的機會究竟有多高。你在設計時,你應該先行設想:「如果我是客戶端程式設計者,倘若API如此設計,我會不會因為一些細微處沒有弄清楚,而以錯誤的方式來使用?」

讓使用API的人不會誤用
正如前文中已經提過的,好的API設計是處處充滿直覺的。好的API設計不會賣弄,不會留下許多暗藏的地雷及陷阱等著客戶端程式設計者去踩。好的產品設計都有所謂的「防呆」機制,好的API設計也不例外。你或許可以假設客戶端程式設計者只是剛入門的程式設計者,他對你的API運作細節也沒有十分了解,但你的API就能防止他走入錯誤的道路。

預防客戶端程式設計者犯錯,除了讓他不容易誤解API的運用方式,也需要在他錯誤用時,加以偵測、防止,同時明確告訴客戶端究竟出了什麼差錯。

有些API函式,並不會檢查客戶端程式碼所傳入參數值是否合法,便直接加以運用,進而引發了不可預期的異常結果。當然,若是有相對應的文件,適度的說明參數的合法或非法值,當然可以降低這種情況發生的機會。但是,更理想的方式,便是API本身便對參數進行檢查,若傳入不允許的非法值,便回傳錯誤代碼,一來阻止一場可能的災難發生,二來也讓呼叫的客戶端程式碼能夠知道自己傳入了錯誤、不被接受的參數,進而快速的修正錯誤。

身為API設計者,把客戶端程式設計者視為你的客戶、使用者,你才會隨時站在他們的立場構思你的設計,那麼你的設計才更有機會成為好的設計。

專欄作者

熱門新聞

Advertisement