接口隔离:(更好的可参考:《C语言接口与实现》一书)
定义为客户端不应该依赖它不需用的接口,在C语言中我们可以把头文件看成一个模块的接口,根据接口隔离原则也就是说这个头文件中只能包含外部需要的接口,但在实际的项目中往往头文件都不符合接口隔离原则。
1:内、外部接口的隔离:头文件中通常包含了模块内部接口(内部类型定义、内部接口声明)和外部接口(外部接口声明)
假设moudle模块对外提供一个fun1接口,模块内部实现需要定义一个结构类型,一般的实现如下:
/*moudle.h*/
typedef struct str_s str_t;
struct str_s
{
int a;
int b;
};
void fun1();
/*moudle.c*/
#include "moudle.h"
void fun1()
{
str_t s = {0};
TODO...
}
客户端在使用接口的时候需要包含moudle.h文件,而该接口并不符合接口的隔离,其内部包含了客户并不需要的一些定义。为了解决这个问题我们可以通过定义不同的头文件来隔离接口,moudle.h定义外部的接口,moudle.inc定义内部接口
/*moudle.h*/
void fun1();
/*moudle.inc*/
typedef struct str_s str_t;
struct str_s
{
int a;
int b;
};
/*moudle.c*/
#include "moudle.inc"
void fun1()
{
str_t s = {0};
TODO...
}
moudle.h包含外部模块需要的接口,外部模块包含moudle.h,moudle.inc包含内部模块需要的接口,在模块内部包含moudle.inc。通过查看模块的.inc和.h文件,我们就可以清晰的理解模块对外和对内提供了什么接口。
2:避免万能头文件的使用,在实际项目中我们经常可以看到一些头文件包含了所有模块的接口声明,客户端只需要包含这个头文件就可以使用任何接口了。
/*global.h*/
#inlcude "moudle1.h"
#inlcude "moudle2.h"
#inlcude "moudle3.h"
....
#inlcude "moudlen.h"
可能带来如下问题:
会显著的增加编译时间,如果项目大,可能大部分的编译时间都花在展开头文件(笔者一个项目测试80%左右的时间)。
不利于代码的框架的理解,客户端无法从包含的头文件中清晰的看到依赖什么外部模块。
3:如果没有隔离接口可能会导致一些误操作:(
不好说) 一个数据获取模块提供两个接口分别从网络和本地缓存获取数据,后台管理模块使用网络接口定时获取数据更新缓存,前台模块使用缓存接口快速获取数据显示,由于没有对接口隔离,后期的维护人员可能并不清楚开始的设计,在前台模块中直接使用网络接口来获取数据显示,导致界面延迟严重。如果一开始就把接口分离,给前台模块提供本地缓存接口,给后台模块提供网络接口,就不会导致问题的出现。