程式的庫設計

發表於2014-04-21

最近在Stack Exchange上面看到一個帖子,是問程式庫設計的指導原則的,“What guidelines should I follow while designing a library?”,有趣的是,很多人都在談論面向設計,各路API設計,還有程式語言設計,唯獨搜尋“程式庫設計”,無論中文還是英文,Google還是百度都找不到太多內容。但是我想,沒有程式設計師會否認庫設計的重要性吧,我想在這裡結合這個帖子談談我的想法。

在這個帖子裡面,votes最高的回答,提到了這樣幾類tips,我在下面簡要敘述一下,其中基礎的部分包括:

  • Pin Map,明確你期望庫主要用來做什麼,但不要把它定得太死,使用者要可以比較方便地做出改變。
  • Working Library,一個工作的庫,如果它連這點都達不到,一定要註明。沒有人希望浪費時間在一個無法工作的程式庫上面。
  • Basic Readme,清晰地描述庫是用來做什麼的,測試的情況等等。
  • Interfaces,介面必須清晰地定義,這可以幫助庫的使用者。
  • Special Functions,特殊的功能,一定要註明,包括在readme文件中註明,以及在註釋中註明。
  • Busy Waits,如果有一些場景需要使用busy wait(我不知道怎麼翻譯),其過程中可能會出現異常,使用interrupt或者其它妥善的方法來處理。
  • Comments,你做的任何的改變都要註釋清楚,明確描述介面和其每個引數,方法是做什麼的,又返回什麼;如果有某個中間方法被呼叫到,就要註明。
  • Consistency,一致性,所有東西,包括註釋。相關的方法要放在一個簡單的程式碼檔案裡面,小但是邏輯一致。

其中的高階部分又包括Detailed Readme,Directory Structure,Licensing和Version Control。

這些都是需要注意的內容,並且大部分對於程式的庫設計來說是基礎要求,但是這些從重要性來說,並沒有說到點上。《C++沉思錄》裡面有這樣一句話:“庫設計就是語言設計,語言設計就是庫設計”,二者從先定義問題域到後解決問題的思路是類似的。我覺得比較重要的需要考慮的事情包括:

考慮庫的目標使用者。這聽起來扯得有點泛,但實際上這是確切的問題,這是開源庫還是你只是在小組內部使用的庫,或者是公司內部使用的?使用者的能力和需求是不一樣的,要求當然不同。

要解決的核心問題。這是上文中Pin Map的一部分,不要重複發明輪子,那麼每一個新庫都有其存在的價值,這個問題既要通用又要具體,“通用”指的是庫總有一個普適性,解決的實際問題對於不同的使用者來說是不一樣的;而“具體”是指庫解決的問題對於程式設計師來說是非常清晰和直接的。例如設計一個庫,根據某種規則把不同的資料型別(XML,BSON或者某種基於行的文字等等)都轉換成JSON。

統一的程式設計風格。很多庫都有自己精心設計的一套DSL,比如鏈式呼叫等等幾種方式,當然,這也和使用的語言有關係。定義一種用起來舒服的程式設計風格對於程式庫的推廣是很有好處的。這也是一致性的一個體現。

內聚的呼叫入口。這和麵向物件的“最少知識原則”有類似的地方,把那些不該暴露出去的庫內部實現資訊隱藏起來,在很多情況下,程式庫不得不暴露和要求使用者瞭解一些知識的時候,比如:

這裡引入了太多的概念,MappingConfig、FileBuilder、DataTransformer等等,整個過程大致是構造了一個資料來源,還有一個資料轉換器,然後這個資料轉換器接受這個資料來源來轉換出最後結果的過程。那麼:

這些象徵著概念的介面和類最好以某種易於理解的形式組織起來,比如放在同一層比較淺的包裡面,便於尋找;

也可以建立一個facade類,提供幾種常用的組合,避免這些繁瑣的物件構建和概念理解,例如:

向後相容。當然,這一點也可以歸納到前文提到的一致性裡面去。我曾經拿JDKHashTable舉了一個例子,它的containsValue和contains方法其實是一樣的,造成這種情況的一個原因就是為了保持向後相容。

依賴管理。依賴管理很多情況下是一個髒活累活,但是卻不得不考慮到。通常來說,任何一個庫考慮自己的依賴庫時都必須慎重,尤其是面對依賴的庫需要升級的時候。如果依賴的庫出了問題,自己設計的程式庫也可能因此連累。

完善的測試用例。通常來說,程式庫都配套有單元測試保證,無論是什麼語言寫的。

健全的文件組織。通常包括教程(tutorial)、開發者文件(developer guide)和介面API文件(API doc)。前者是幫助上手和建議使用的,中間的這個具備詳盡的特性介紹,後者則是傳統的API參考使用文件。

相關文章