最近在设计一个方案,其中需要设计一个API给各个模块使用。这个API需要实现一个 Java版本,一个C版本。深知设计一个API不是件容易的事情,查询了一些资料进行了一次实践,分享一下经验。翻阅了如下几个文档:《C Interfaces and Implementations: Techniques for Creating Reusable Software 》,“Linux Kernel Design Pattern”及其http://wiki.apidesign.org/wiki/APIDesignPatterns,AOSP源代码等。
1,选择API是同步方式,还是异步方式
https://whatis.techtarget.com/definition/synchronous-asynchronous-API
从调用者角度来看,同步方式使用起来更加方便,直接调用返回结果即可,但是对于耗时的调用,需要调用者自己去做异步处理,例如在Android平台启动一个AsynTask去调用。对于异步API,调用者不需要等待,通过注册一个callback类进行通知,但对调用者来说,使用相对复杂,需要自己处理时序和同步关系;我设计的API需要跟云端进行通信,耗时不定,因此设计成了异步API方式,样例代码如下:
class PermissionManager {
public void request(int deviceId, PermissionCallback callback) {
...
}
};
interface PermissionCallback {
void onFinish(PermissionStatus status);
}
...
设计成异步API,对于调用者来说用起来相对复杂。如果是同步方式,直接通过参数或者返回值返回结果;
class PermissionManager {
public void request(int deviceId, PermissionStatus status) {
...
}
};
...
后续考虑增加同步接口。
2,API中引入所有类型和概念要完备自洽
上面 request API参数中引入一个DeviceId概念,那就要定义和说明DeviceID获取方式;示例代码request接口中使用了int类型的deviceId,其实问题比较多。这个id有谁定义有谁产生,在这个API是无法闭环的。解决这个问题有两个方法:如果这个ID是其他模块定义的,接口说明中需指向定义这个Id的子系统及其具体定义;如果这个ID是自己模块的概念,不要用int类型,应该定义一个class DeviceId类型,并且有直接或间接产生DeviceId的API。
class DeviceId {
...
};
3,C语言接口和实现分离,不暴露任何类型实现细节
经典《C Interfaces and Implementations: Techniques for Creating Reusable Software 》核心思想就是将Interfaces与实现进行分离,不暴露任何实现细节。大家可以参考Stack数据结构的定义。
4,C接口中异步callback如何实现?
1中描述的接口形式是java接口,如果是C接口,需要使用函数指针实现callback;在C语言中没有类的概念,同时为了彻底隐藏实现细节,不能使用struct结构。
// permission_manager.h 使用文件名表示模块
struct PermissionStatus;
typedef PermissionStatus* PermissionStatusPointer;
typedef void (*PermissionCallback)(PermissionStatusPointer status);
void Request(int deviceId, PermissionCallback callback);