在SDK开发中,接口(全称应用程序接口
,也就是大家熟知的API
)是很重要的一块,它是SDK与上层应用程序之间进行交互的桥梁,接口规范是确保接口的一致性、完整性和可维护性的重要手段。
为了保证接口的可读性和可维护性,在接口设计的时候就需要遵循一定的原则和规范,如接口命名的原则、接口设计的原则、接口的版本管理等。
1. 接口命名的原则
1.1. 一致性
一致性是指在整个SDK接口中保持命名的一致性,包括:风格的一致性和术语的一致性。
命名风格一致
在整个SDK中,接口命名应采用一致的风格,例如驼峰命名法、下划线命名法等。
命名术语一致
使用统一的术语来描述相似的功能或概念,避免在不同接口中使用不同的术语。
相近的词汇只用一种: 举一个例子,要表示“目录”的概念,
catalog
和outline
都可以。但不能在A接口了用catalog
(如openCatalog
),B接口里用outline
(如closeOutline
),这一种错误的命名方法。正确的命名是:要么全部用catalog
(openCatalog
、closeCatalog
),要么全部用outline
(openOutline
、closeOutline
)。相反的词汇要成对出现: 如使用了
begin
表示开始,则要使用end
表示结束,而不应该用stop
;同样的,如果使用了start
就应该用stop
。在实际项目中见到最常见的错误混用是start
和end
,这种命名其实是不太优雅的。程序员开发中常见的成对出现的单词可以参见《附录A-计算机术语中成对出现的单词》。
前缀和后缀一致
如果某些接口具有特定的前缀或后缀,应在整个SDK中保持一致。
举例来说,如果某些接口需要使用前缀来表示其功能,例如create
、get
等,那么这些前缀应在整个SDK中保持一致。例如,创建用户接口可以命名为createUser
,创建文件文件接口命名为createFile
;获取用户接口可以命名为getUser
,获取文件的接口命名为getFile
。
命名空间一致
相同模块的接口采用相同的命名空间,且如果加了命名空间,那么该模块下的所有接口都需要加上命名空间。不能部分接口加命名空间,部分接口不加。
对于C++的SDK,通常整个SDK会使用一个相同的命名空间,如:cutl
表示C++通用工具库的命名空间。当然如何SDK比较复杂,包含多个大的模块,也可以使用二级命名空间,如:cutl::fs
表示文件系统模块的命名空间,cutl::time
表示时间处理模块的命名空间。STL就有这种典型的应用,如:std::chrono
。
版本一致
在接口版本更新时,已有的接口,接口名必须保持一致。这样我们的接口才能保持向后兼容。
举个例子,我们有一个接口用于展示文本信息,这个接口v1.0的版本只支持纯文本的展示,接口定义如下:
1 | void showText(const std::string& text); |
后面需求变更了:不仅要支持纯文本的展示,还要支持HTML格式的展示和Json格式的展示。这个时候,为了保证我们接口的向后兼容性,V2.0的接口可以这么定义:
1 | enum TextFormat { |
这里增加一个format
参数,可以拓展新增的格式。因为format
提供了与V1.0版本含义一致的默认值,且接口名保持不变,所以这个接口能够做到向后兼容。
1.2. 简洁性
命名应简洁明了,避免冗长的命名,确保接口名称直击接口的核心功能。
避免冗余
例如,getUser
比getUserFromDatabase
更简洁。
使用缩写
可以通过适当的缩写来简化接口的名称。如getUserInformation
可以缩写成getUserInfo
。但使用缩写时,需要遵循以下几个原则:
- 专业术语中的专用书写,如
id
、dns
。 - 要是程序员中大家普遍熟悉的缩写。如
num
表示number
。 - 缩写要可发音方,方便开发人员进行交流。如表示“当前时间(
currentTime
)”的缩写用curTime
, 而不是crtTime
(crt
无法发音,只能念字母c
、r
、t
,而且容易与CRT(C RunTime
)术语混淆)。
程序开发中常见的缩写,可以参见《附录B: 计算机术语中常见的单词缩写》
1.3. 描述性
接口名应能准确的描述其功能或用途。通常用两种方式来命名我们的接口:
- 名词: 常用于获取信息或展示信息的接口,如
userList
表示用户列表,fileManager
表示文件管理器。 - 动词+名词: 常用于表示执行某项操作,如
createFile
表示创建文件、openFile
表示打开文件、closeFile
表示关闭文件。
2. 常见的命名法
2.1. 驼峰命名法
驼峰命令法(Camel) 是指混合使用大小写字母来构成变量和函数的名字,当变量名或函数名是由一个或多个单词连结在一起构成的唯一识别字时,第一个单词以小写字母开始,从第二个单词开始以后的每个单词的首字母都采用大写字母。如:createFile
、openFile
,这样的变量名看上去就像骆驼峰一样此起彼伏,因此被称为驼峰命名法。
Java
/Objective-C
/Swift
语言默认就是使用驼峰命名法。
2.2. 帕斯卡命名法
帕斯卡命名(Pascal) 也叫大驼峰法,与驼峰命名法类似,不过骆驼命名法是首字母小写,而帕斯卡命名法是首字母大写,如:CreateFile
、OpenFile
。
2.3. 匈牙利命名法
匈牙利命名法通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识出变量的作用域,类型等,命名的格式如:[属性]_[类型(小写)][变量名(首字符大写)]
,如m_nAge
,m_
表示该变量是成员变量,n表示该变量是整型;nAge
表示局部变量,类型为整型。表示数据类型的前缀符号也可以多个同时使用,如m_lpszStr
表示指向一个以\0
字符结尾的字符串的长指针成员变量。
属性:
g_
: 全局变量m_
: 类成员变量s_
: 静态变量c_
: 常量
类型:
a
: 数组(Array)b
: 布尔值(Boolean)by
: 字节(Byte)c
: 有符号字符(Char)cb
: 无符号字符(Char Byte,并没有神马人用的)cr
: 颜色参考值(Color Ref)cx,cy
: 坐标差(长度 Short Int)dw
: 双字(Double Word)fn
: 函数(Function)h
: Handle(句柄)i
: 整形(Int)l
: 长整型(Long Int)lp
: 长指针(Long Pointer)n
: 短整型(Short Int)np
: 近程指针(Near Pointer)p
: 指针(Pointer)s
: 字符串(String)sz
: 以 Null 做结尾的字符串型(String with Zero End)w
: 字(Word)
匈牙利命名法起源于微软的内部,微软的C++项目用的比较多。但因其规则的复杂和臃肿,现在这种命名法在逐步被淘汰,新的项目越来越少用这种命名法了。
2.4. 下划线命名法
下划线命名法将多个单词的变量用下划线(_)进行分割,单词本身全部用小写,如create_file
、open_file
。
3. 接口设计原则
3.1. 三大原则
面向对象设计中有五大基本的原则,他就是SOLID
原则,是五大原则首字符的搜写。
- S 单一职责原则
- O 开发关闭原则
- L 里氏替换原则
- I 接口隔离原则
- D 依赖倒置原则
这里其中有三大原则同样适用于API接口的设计。他们分别是:
单一职责原则
单一职责原则(Single Responsibility Principle
),简称SRP
。每个接口应该只有一个明确的目的和职责。这样可以确保接口的简洁性和高内聚性,避免接口功能过于复杂和冗余。
接口隔离原则
接口隔离原则(Interface Segregation Principle
), 简称ISP
。一个接口不应该定义太多的功能,只提供这个接口必要和相关的功能,应用层不应该依赖它不需要的接口。接口应该被拆分成更小的、更具体的部分,以减少对未使用方法的依赖。
依赖倒置原则
依赖倒置原则(Dependence Inversion Principle
), 简称DIP
。应用层模块不应依赖于低层模块的具体实现,而是依赖于抽象,即接口。所有我们的SDK应该只提供接口与应用层进行交互,而不应该暴露具体的实现。
3.2. 版本管理与向后兼容
除了上面的三大原则外,还有两点需要注意:SDK的版本管理和向后兼容。
3.2.1. 版本管理
版本管理: 是说SDK必须要有版本号,并提供获取版本号的接口(如:getVerstion()
),每次发布SDK时,必须保证版本号增量递增。这样上层应用就可以进行一些逻辑的判断,如:判断某个特定的接口是从哪个版本开始支持的。
3.2.2. 向后兼容
向后兼容是说SDK的每一次发版,所有接口都必须兼容低版本。要做到向后兼容,就必须遵循一个原则:
接口只能新增和修改,不能删除。
新增: 这个不需要解释,肯定不会有兼容性的问题。
删除: 对于已经发布的接口,即使你想废弃,也不能直接删除,只能在接口的注释里打标签,如:@deprecated
,表明此接口已废弃,后期将不再维护,上层应用应该尽快用新的接口替代。
修改: 也需要保证修改后的接口与修改前的接口兼容,如何做到这一点呢?可以有两种策略:
- 接口的名称、入参和出参都保持不变,只修改接口的具体实现。
- 新增参数但提供默认值,且默认值的行为与旧版本保持一致。这个在 “1.1 一致性” 小节的 版本一致 中有举例,这里不再赘述。
4. 完整的注释
对于需要对外开放的SDK,每一个接口都需要有完整的注释。这样调用方才能更具体的了解每个接口的功能和用法。关于接口注释更详细的介绍,将会在下一章进行讲解。
历史文章推荐: