結合框架、庫、SDK討論API

框架、庫、SDK的區別

框架(Framework)

通常指的是爲了實現某個業界標準或完成特定基本任務的軟件組件規範,也指爲了實現某個軟件組件規範時,提供規範所要求之基礎功能的軟件產品。框架是針對開發人員的規範或軟件產品,一般爲開發更上層應用提供基礎功能,可開發框架一般有適用的特定領域,比如作爲網絡程序開發基礎中間件的ACE框架,作爲桌面應用程序開發框架的Qt、MFC。

框架面向的使用者一般是開發人員,因爲框架提供的是開發標準。

集成時要求使用者對框架的原理有較深入理解,不可能將框架視作黑箱子使用。這是框架與庫或者SDK產品最大的區別。

常見的開發框架有:

c++

ACE, Qt, MFC

java

Spring

python

Django

 

庫(Library)

用於開發軟件的子程序集合。庫和可執行文件的區別是,庫不是獨立程序,他們是向其他程序提供服務的代碼。庫是封裝好的代碼,通過調用開放出來的API獲取相應的功能,比如網絡庫提供網絡相關的功能,深度學習庫提供深度學習相關功能,此處的API泛指用戶使用庫功能的接口,包括類、方法、變量等。

庫面向的使用者還是開發人員,爲了實現真正的業務功能,往往需要使用多個庫的功能。

c++庫的集成方式有兩種,顯式集成和隱式集成,開發者完全可以以黑匣子的方式集成庫提供的功能,而不必關心庫內部的運行原理。

常見的庫有:

c++

opencv, libevent, libcurl, libuuid, openssl

java

NIO, HttpClient

python

numpy, sicpy

SDK(Software Development Kit)

軟件開發工具包,指輔助開發某一類軟件的相關文檔、範例和工具的集合。其中也包括SDK使用到開發框架和類庫。SDK和庫其實比較類似,也是爲使用提供功能,但是不同的是,庫提供的代碼是比較底層的功能,比如提供網絡傳輸的功能,而SDK提供的是業務應用領域的功能,比如百度地圖SDK提供地圖業務相關的接口,大華提供的NetSDK提供控制大華攝像頭相關的接口。SDK產品一般是企業針對其具體業務設計開發,是企業對外提供服務的手段之一。一款完整的SDK產品,不經包括完善的文檔說明、使用實例、測試用例等。SDK產品開發,會使用開發框架和庫。

SDK產品面向的使用者是客戶的開發人員。

SDK集成方式類似庫。

 

c/c++庫和SDK產品API設計原則

c/c++開發的庫集成方式通常是通過引入頭文件,用戶通過頭文件可以找到動態庫中接口的地址,頭文件應該分爲面向開發者的內部頭文件和麪向使用者的外部頭文件。外部頭文件的編寫應遵循最小化需求原則,用戶不需要的接口、數據結構或者需要對用戶隱藏的接口禁止放在對外的頭文件中,windows中需要用導出符號修飾導出的數據類型和函數。另外不肯避免庫和SDK很可能引入其他的庫和SDK,爲了避免當更上層應用也引入同一個依賴庫時出現鏈接錯誤(重複的引用),庫和SDK不應該對外暴露其依賴庫的接口,只要對外暴露依賴庫接口。windows下即使A,B兩個庫分別以靜態庫方式引入同一個依賴庫,而上層應用有同時引入A,B兩個庫,也不會出現鏈接錯誤重複的引用。linux下方法還需要研究。

除了需要給用戶提供接口頭文件以外,還需要考慮用戶是否以顯示方式集成。顯示方式加載動態庫時,客戶不使用頭文件的,且只能導出函數。多語言調用也是一個需要考慮的問題,c/c++開發的庫或者SDK往往是上層軟件中的組件,比如作爲通訊組件的libev,而上層應用node.js代碼使用js開發,因此庫和SDK提供的接口必須支持跨語言調用,java、python等僅支持從動態庫顯式加載c接口,結構體數據結構則需要在相應語言中以規定的形式重新定義,比如java中jna\jni。所以在設計API時,除了能夠提供c++的外部接口(只能通過頭文件方式集成),還應該提供c接口和POD數據結構,方便其他語言定義對應的接口文件集成及方便顯示集成。

用戶在使用庫提供的C接口和POD類型參數時,POD類型的數據由於是c風格的,內存申請、初始化、內存釋放都由使用者控制,當這些數據通過接口從應用層傳入庫內核層後,其中某一些數據比如一些配置相關的數據將被庫保存到內核層。這個時候一個問題時,當這些數據是以指針的形式被傳入並保存時,是禁止庫內核層直接保存指針的,因爲內核層不能判斷這片內存數據是堆內存還是棧內存,也就不知道是否需要主動釋放內存;並且如果庫提供的是異步方法,當方法真正執行的時候無法確定指針指向的內存數據是否有效。所以一個關於API參數的原則,庫API的參數對接口內部的對象來說默認都應該是一次性的,一旦接口返回,參數提供的數據自動失效,若果保存參數提供的數據則需要深拷貝。在c++開發庫的時候,外部數據結構是POD的,提供內部使用的數據結構則可以是面向對象的class,這樣可以充分利用c++的一些特性實現更智能的內存管理。

因爲c語言風格的結構是不支持重載的,接口名無法重複使用,因此爲了防止接口數量膨脹。可以類似功能共用同一個接口封裝,然後通過參數區分執行那個內部調用。以配置接口爲例:

#define CONF_LINE 1
#define CONF_BAR 2
#define CONF_ PIE 3
struct ConfLine{...};
struct ConfBar{...};
struct ConfPie{...};
int Configure(int confType, void* data, size_t size);
...
//調用
struct ConfLine confLine;
Configure(CONF_LINE, &confLine, sizeof(confLine));

配置類接口及參數設計

設計配置接口時,可以將對同一功能的配置放在一個數據結構中,比如在eharts++的設計中,Line圖的所有配置項都放在LineConf中,如下表:

#define CONF_LINE 1
#define CONF_BAR 2
#define CONF_ PIE 3
struct LineConf{...};
struct BarConf{...};
struct PieConf{...};
int GetConfigure(int confType, ARG_OUT void* data, size_t size);
int SetConfigure(int confType, ARG_IN void* data, size_t size);
...
//調用
struct LineConf lineConf;
GetConfigure(CONF_LINE, &lineConf, sizeof(lineConf));
SetConfigure(CONF_LINE, &lineConf, sizeof(lineConf));

此時的,配置是按照業務(不同的圖表)進行初步劃分的,這個劃分的粒度比較大。當一個圖表的配置項比較多的時候,每次需要按照全部配置項進行配置是麻煩的,可能會有較多的內存拷貝操作。所有一種方案是,進一步縮小配置的粒度,劃分標題,x座標,y座標,表值作爲更細的配置粒度,如下:

struct LineConf
{
    struct TitleConf
    {
        bool flag;
        ...
    };
    struct XAxisConf
    {
        bool flag;
        ...
    };
    struct YAxisConf
    {
        bool flag;
        ...
    };
    struct SeriseConf
    {
        bool flag;
        ...
    };
    ...
};

併爲每一個細粒度的配置子項增加flag,標識本配置是否啓用。當調用GetConfigure接口,接口只獲取flag!=false配置子項的配置值,當調用SetConfigure接口時,接口只會按照flag!=false配置子項做配置操作。這個方案可以避免多餘的操作帶來的性能損耗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章