魏礼俊
用户二次开发软件的兼容性架构设计
魏礼俊
针对企业生产实践中经常遇到的、用户在设备供应商提供的接口库函数基础上二次开发的软件,因供应商接口库函数版本升级带来的软件兼容性问题,以C、C++语言接口库函数为例,分析可以采取的软件措施及其利弊,提出优化的软件架构设计方法,防患于未然,力图使用户二次开发软件以最小的代码修改量与维护复杂度,事半功倍,实现与供应商提供的系列软件接口库函数全兼容,从而达到设备效能得以最大发挥且稳定可靠运行的最终目的。
软件兼容性;软件接口;架构设计
在企业生产实践中,经常需在供应商提供的软件接口库基础上进行二次开发,才能最大发挥相应设备的功能。对于同类款式的系列设备,有时供应商会对软件接口库进行版本升级,迫使在供应商前款软件基础上进行二次开发的用户软件也需作同步更新,否则无法继续使用。用户软件在开发初始版本时该如何设计架构,便于应对后续可能发生的供应商软件升级带来的软件兼容性问题,实现用户软件修订时代码改动量最少、维护最简便,从而运行最可靠的最终目标,值得认真考虑。
一般而言,供应商软件接口库升级不会没有章法,除新增功能接口函数外,原有功能接口函数大都仅改变函数名称(防止新旧库文件同时存在时的同名问题可能引起用户软件无法编译、运行)。即使有个别函数发生了重大改变,也可视其为新增功能接口函数。下面将以C、C++语言接口库函数为例,以伪代码形式介绍解决用户二次开发软件在兼容性方面需作的架构考虑。
对于用户二次开发软件来说,能正确定位、使用接口库中相应函数是第一要务。原有功能接口函数需重新定位、使用,而新增功能接口函数在二次开发的软件代码中需作全新封装处理。
总体来看,有3种基本方法处理接口库函数升级问题:使用预编译项、使用配置文件或用户界面选项、使用函数指针数组。前两种方法比较直观,都是使得程序按if-else选择结构进行分支处理,但后续代码维护量大;而使用函数指针数组的方法则比较含蓄,且后续代码维护简易。以下将逐一解析上述3种处理方法处理各版本接口函数的过程,比较利弊,最后给出较为优化的解决方法。
2.1 使用预编译项
假设用户二次开发软件针对供应商接口库某一接口函数的初始版本是:
RET_TYPE User_func_examp(……)
{
……
PARAM_A_TYPE parama;
API_A_TYPE_func(parama);
……
}
供应商接口库函数版本升级后,用户软件采用类似如下的预编译项进行不同接口库同一功能接口函数选择:
RET_TYPE User_func_examp(……)
{
……
#ifdef VersionA
PARAM_A_TYPE parama;
API_A_TYPE_func(parama);
#elif VersionB
PARAM_B_TYPE paramb;
API_B_TYPE_func(paramb);
……
#else
PARAM_X_TYPE paramx;
API_X_TYPE_func(paramx);
#endif
……
}
用户软件中所有用到的供应商接口库接口函数都需作类似处理。处理完毕后,用户还需在编译器未编译到上述代码之前的合适位置显式地定义当前所使用的版本,例如:#define VERSION_B;或者是在编译器编译选项中添加适当的编译选项,如-D VERSION_B。编译后,重新运行。
可以看出,不论是否有新增功能函数,各版本所有功能接口函数都能进行类似处理,但多个C、C++文件需逐一作同步更新,程序修改、维护都颇为繁琐。
2.2 使用配置文件或用户界面选项
同样假设用户二次开发软件针对供应商接口库某一接口函数的初始版本同上。现在供应商接口库函数版本升级后,用户软件可在某个文件路径下放置配置文件config.txt,文件内容如下:
Version_Tye:
在该语句后可输入如TYPE_A、TYPE_B、……、TYPE_X等版本信息,用户软件在读入使用者填入的版本信息并判断信息有效性后,转入对应供应商接口库函数运行分支。用户软件也可在用户界面中进行版本信息读取、处理、有效性判断,同样转入对应供应商接口库函数运行分支。例如:
RET_TYPE User_func_examp(……)
{
……
switch(Version_Type)
{
case TYPE_A:
PARAM_A_TYPE parama;
API_A_TYPE_func(parama);
break;
case TYPE_B:
PARAM_B_TYPE paramb;
API_B_TYPE_func(paramb);
break;
……
case TYPE_X:
PARAM_X_TYPE paramx;
API_X_TYPE_func(paramx);
beak;
default:
出错记录;
break;
}
……
}
编译后,重新运行。
如使用预编译项类似,各版本所有功能接口函数都能进行这样的case处理;但增加过多的case使得函数路径增加,程序维护难度加大。
2.3 使用函数指针数组
同样假设用户二次开发软件针对供应商接口库某一接口函数的初始版本同上;在供应商接口库函数版本升级后,该接口函数可能有如下两种变化:
如果各版本仅改变了函数名称,则可定义如下形式的接口函数跳转表:
typedef RET_TYPE (*FP_Func[FUNC_NUM_MAX])(PARAM) = {API_A_TYPE_func,
API_B_TYPE_func,
……
API_X_TYPE_func};
用户二次开发软件接口函数按如下方式使用该跳转表:
RET_TYPE User_func_examp(……)
{
……
FP_Func fun_handle;
(*FP_Func[Version_Type])(PARAM);
……
}
上述示意代码中的Version_Type可来自于配置文件或用户界面选项。编译后,重新运行。
如果各版本相互差异较大,如参数列表或返回参数类型已完全改变,则可以使用前述预编译项、配置文件或用户界面选项方法加以解决;或者按上述接口函数跳转表思路进行全新的二次开发。
可以看出,对于各版本大部分功能接口函数来说,适用函数指针数组的方法,代码维护量大为降低,仅需维护各个接口函数跳转表。但对于每个用户接口函数,都需要有类似User_func_examp的处理过程,因此,仍有必要继续优化代码架构。
从使用者习惯来看,一般先选择,正确设置某一类型参数后,即可以开始运行特定功能函数。为此,需要斟酌选用现有软件方法加以合理使用。二次软件开发可作如下优化处理过程:
对供应商提供的接口库函数进行全面梳理,将版本虽不同,但接口函数的参数列表、返回参数中有某几项类型对应相同的集中在一起视作某一类型函数。例如:若干个无参、返回参数类型相同的接口函数;若干个输入参数、返回参数类型相同,无其余参数的接口函数;若干个输出参数、返回参数类型相同,无其余参数的接口函数等;
单列出各版本相互差异较大的个别接口函数;
经过梳理后,对于可以归并为某几个类型的函数,分别用如下的二维函数指针数组进行定义:
typedef RET_TYPE (*FP_Fun2c[VERS_NUM_MAX][FUNC_NUM_MAX])(PARAM) =
{{TYPE_A, A_FUNC_NBR1},
{TYPE_A, A_FUNC_NBR2},
……
{TYPE_A, A_FUNC_NBRN},
{TYPE_B, B_FUNC_NBR1},
{TYPE_B, B_FUNC_NBR2},
……
{TYPE_B, B_FUNC_NBRN},
……
{TYPE_X, X_FUNC_NBR1},
{TYPE_X, X_FUNC_NBR2},
……
{TYPE_X, X_FUNC_NBRN}};
对应二维函数指针数组的接口函数跳转表,可以采用与前述类似的处理过程:
RET_TYPE User_func_examp(……)
{
……
FP_Func2 fun_handle;
(*FP_Func2[Version_Type][Func_Number])(PARAM);
……
}
Version_Type、Func_Number来自用户软件界面或者配置文件。此时,Version_Type(枚举值数目不超过VERS_NUM_MAX)、Func_Number(枚举值数目不超过FUNC_NUM_MAX)可作如下枚举定义:
Typedef enum
{
TYPE_A,
TYPE_B,
……
TYPE_X
} VERSION_TYPE_ENUM;
Typedef enum
{
FUNC_NBR1,
FUNC_NBR2,
……
FUNC_NBRN
} FUNC_NBR_ENUM;
而对于那些不能集中的个别接口函数,则仍然使用前述的预编译项、配置文件或用户界面选项方法加以解决;或者按上述接口函数跳转表思路进行全新的二次开发。
而为了便于维护,所有的函数指针数组可以放在同一个.h头文件中。如此处理后,有望从设计层面将版本升级带来的修订、维护成本大幅降低,即已有接口函数可以继续运行,只需扩充新增类似功能的接口函数。
良好的软件架构设计将为后续软件编码、维护带来极大便利,这是共识。但在实际工程中,这一点却又经常被有意无意地忽视。现在忽视架构设计,仅仅停留在程序可以运行,代码一蹴而就,那只会为将来事倍功半的程序维护买单,而且也不利于代码质量的提高。本文试图以管窥豹,探讨一下用户二次开发软件架构优化设计带来的程序简洁、健壮、易维护性方面的好处,以期更好地服务于实际工程。
[1] Prata S.著,云巅工作室 译,C Primer Plus 中文版[第五版] [M],北京:人民邮电出版社,2005
[2] Stephen Prata著,孙建春、韦强译,C++ Primer Plus 中文版[第五版],[M]北京:人民邮电出版社,2005
[3] 钱能著,C++ 程序设计教程[第二版][M],北京:清华大学出版社,2005
[4] 凌坚、隋成华,基于组件结构的软件二次开发接口的设计与实现[J],计算机工程, 28(2),2002年2月
[5] 李江红、韩正之,Matlab和C++接口中函数注册的实现[J],计算机应用,2000年4月第20期
[6] 朱传安、潘大夫,Matlab与C/C++混合编程接口及应用[J],微计算机信息(管控一体化),2010年第26卷第5-3期
Design of Compatible Framework in Software Further Development of Users
Wei Lijun
(SMEE, Shanghai 201203, China)
This paper aims at the software compatible problems caused by version upgrade of the interface library functions provided by the providers. It is often come up in the production practice when the users do the further development of their software on the basis of these interface library functions. While taking the application software interfaces written in C/C++ language as an example, this paper analyzes the software measures and their advantages and disadvantages. Then it puts forward the optimized framework design method, trying to nip in the bud to provide the user’s software further development with minimum modification of codes and least complication degree of maintenance. It realizes the comprehensive compatibility with the interface library functions of the serial software provided by the providers to obtain the final target of stable and reliable running of the equipments and their maximum efficiency.
Compatibility of Software; Software Interface; Framework Design
1007-757X(2016)04-0048-03
TP391.41
A
(2015.08.17)
魏礼俊(1976-),男,安徽省当涂县,上海微电子装备有限公司,分系统与整机软件开发,工程师,工学硕士,研究方向:应用软件开发、算法仿真分析、图像处理算法编程等,上海,201203