结合框架、库、SDK讨论API

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/q8250356/article/details/100640972

框架、库、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配置子项做配置操作。这个方案可以避免多余的操作带来的性能损耗。

猜你喜欢

转载自blog.csdn.net/q8250356/article/details/100640972