降低模块间的耦合性有不少方法,主要以下几种:
其中API直接调用方式,虽然耦合性还是很高,但是其简单易用,使用较为广泛,本文将主要讨论:
为降低模块间耦合性,API函数应如何设计。
NOTE: 我个人比较倾向使用模块化的函数指针,以后再谈。
最朴实的函数,用头文件暴露出去,供调用者们调用。 例如:
#if HELLO_MODULE//hello.hextern int api_say_hello(int times);//hello.cint api_say_hello(int times){... ... return SUCCESS;}//caller.c#include "hello.h"int nret = api_say_hello(3);
hello既然是模块,最基本的要求是:可以开关的;此方案开启时当然没有问题,一旦关闭编译都过不了。
//caller.c#if HELLO_MODULE#include "hello.h"int nret = api_say_hello(3);#endif
一旦API多起来,调用者的代码中将充斥太多的ifdef ... else ... endif, 代码可维护性很差。
Mock是单元测试中常用方法,意思是:模仿、假造、山寨……。 如何山寨一个API呢?
示意代码还是以方案一为基础,只修改头文件即可:
//hello.h#define NOT_SUPPORT -1#if HELLO_MODULEextern int api_say_hello(int times);#else#define api_say_hello(arg) NOT_SUPPORT#endif
如果没有定义HELLO_MODULE,将执行 else中的代码:
#define api_say_hello(arg) (-1)
此时传入的参数没有使用会产生warning,一般不碍事,但如果代码要求较高,Makefile中 加入了-Werror(for 0 warning)这就是一个不得不面对的问题了。
去除warning的思路是在Mock API声明中加入一些特别处理:
//hello.h#define UNUSED_PARAM(x) ((void)x)#define API_MOCK_FUN() NOT_SUPPORT#define API_MOCK_FUN1(ag1) UNUSED_PARAM(ag1); API_MOCK_FUN() ... ...#if HELLO_MODULEextern int api_say_hello(int times);#else#define api_say_hello(arg) API_MOCK_FUN1(arg)#endif
当调用方法为int nret = api_say_hello(3); 没有任何问题,但如果调用者这样使用:
if(api_say_hello(3) ==SUCCESS){ //... ...}
宏展开后:if(((void)3);NOT_SUPPORT == SUCCESS)if括号内不能有分号。
将 API_MOCK_FUN1 的实现用 函数表示,参数类型为(void *),使用宏转换后传入:
//api_mock.hextern int api_mock_fun(void);extern int api_mock_fun1(void* arg1);extern int api_mock_fun2(void * arg1, void * arg2)...#define API_MOCK_FUN() api_mock_fun()#define API_MOCK_FUN1(ag1) api_mock_fun1((void*)arg1)#define API_MOCK_FUN2(ag1, arg2) api_mock_fun2((void*)arg1, (void*)arg2)...//api_mock.cint api_mock_fun(){ UNUSED_PARAM(arg1); return NOT_SUPPORT;}//api_mock.cint api_mock_fun1(void * arg1){ UNUSED_PARAM(arg1); return NOT_SUPPORT;}int api_mock_fun2(void * arg1, void * arg2){ UNUSED_PARAM(arg1);UNUSED_PARAM(arg2); return NOT_SUPPORT;}... ...
联系客服