程序的庫設計

jopen 10年前發布 | 7K 次閱讀 程序

  最近在 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 config = new MappingConfig ();
config.put (MappingConfigConstants.ENCODE, "UTF-8");
FileBuilder fileBuilder = new StandardFileBuilder (mapping);
InputStream stream = fileBuilder.build () .getInputStream ();
DataTransformer<XMLNode> transformer = new XMLDataTransformer (...);
...
transformer.transform (stream);

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

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

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

XXFacade.buildCommonXMLTransformer ();

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

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

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

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

                    <span id="shareA4" class="fl">                          </span>                 </div>
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!