降低耦合(1)API函数设计方法讨论简介降低模块间的耦合性有不少方法,主要以下几种: * 函数指针,(e.g. callback) * 消息通讯(e.g. 订阅机制) * 插件机制 * ... 其中API直接调用方式,虽然耦合性还是很高,但是其简单易用,使用较为广泛,本文将主要讨论:
NOTE: 我个人比较倾向使用模块化的函数指针,以后再谈。 方案一:最原始的方法最朴实的函数,用头文件暴露出去,供调用者们调用。 例如: #if HELLO_MODULE //hello.h extern int api_say_hello(int times); //hello.c int api_say_hello(int times) { ... ... return SUCCESS; } //caller.c #include "hello.h" int nret = api_say_hello(3); 问题hello既然是模块,最基本的要求是:可以开关的;此方案开启时当然没有问题,一旦关闭编译都过不了。 方案二:可开关
问题一旦API多起来,调用者的代码中将充斥太多的ifdef ... else ... endif, 代码可维护性很差。 方案三:Mock APIMock是单元测试中常用方法,意思是:模仿、假造、山寨……。 如何山寨一个API呢?
//hello.h #define NOT_SUPPORT -1 #if HELLO_MODULE extern 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)这就是一个不得不面对的问题了。 方案四:Mock API no 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_MODULE extern 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括号内不能有分号。 方案五:Mock API使用函数替换宏将 API_MOCK_FUN1 的实现用 函数表示,参数类型为(void *),使用宏转换后传入: //api_mock.h extern 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.c int api_mock_fun() { UNUSED_PARAM(arg1); return NOT_SUPPORT; } //api_mock.c int 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; } ... ... |
|