对于SDK而言,接口是链接SDK与客户产品的纽带,接口设计的优劣是衡量SDK产品易用性的重要指标。糟糕的SDK接口不只仅给开发者带来的难用的主观印象,更有可能增长客户的开发成本,甚至影响产品质量。
笔者在从事SDK开发的几年起初并不重视,在一次次填坑的过程当中也逐渐意识到优秀的SDK接口设计必需要思考如下几点:java
风格统一
统一的接口设计风格不只仅是为了给开发者留下专业的印象。更进一步的,它能够传递给开发者SDK的设计理念。开发者经过这种风格上的暗示,能够更直观,不易犯错的调用SDK功能。
举例来讲,咱们能够设计SDK全部接口都经过init方法初始化,uninit方法反初始化。开发者一旦接受了这种设定,会潜意识的注意有没有调用过uninit释放资源。android
平台惯例
一款SDK产品底层一般是跨平台实现,上层则是针对不一样平台作一层封装,封装层特别要考虑的是平台的开发惯例。因为对接SDK的开发者一般是专一于某一特定平台的开发,不符合本平台接口命名风格,设计惯例的接口会给他们带来额外的学习成本,增长对接难度。c++
升级扩展
接口设计考虑升级扩展这一点对每个负责持续迭代的产品的程序员来说都是基本的要求。可是对于SDK产品,这一点显得尤其重要。缘由在于,通常接口设计若是不合理能够经过后期迭代重构来补救,而SDK一旦发布以后,想要客户修改代码来升级SDK的成本是很高的。接口兼容性若是作的很差,客户就不会愿意升级,随之带来的问题是维护多个版本给咱们自身带来的巨大的支撑压力。程序员
注释说明
注释和接口是SDK的一体两面,它在SDK中有着和接口同等重要的做用。SDK没有一个接口是多余的,一样,没有一个接口是能够被容许没有注释的。咱们在开发SDK时,始终要预设这样一个前提:开发者不会看咱们的集成文档。所以,咱们只有尽量的丰富注释,才有可能最大程度将问题消灭在开发者翻阅文档或是提问以前。api
针对上述几个注意点,咱们基于通用编码规范,平台命名规范,以及行业通用惯例制定了本身的SDK接口设计规范。目前尚未特别完善和细化,但有了一个统一的标准以后,相信对将来的接口设计会有一个原则性的指导。咱们的SDK主要是针对移动端iOS/android平台,采用的语言主要是oc/java,其余平台的设计规范能够作一个参考。数组
类/接口的命名必须使用名词。多线程
// good case
public interface AliyunIRecorder; // android; 接口加I区分
// bad case
public interface AliyunIImport; // android; import不是名词
复制代码
方法/函数名足够清晰易懂,除了行业内通用的专有名词,其余状况下不能使用缩写。异步
// good case
void setExposureCompensationRatio(float value); // android; 方法名即解释
// bad case
- (int)removeTransitionAtIndex:(int)clipIdx; // iOS; 参数名应该使用全称clipIndex
复制代码
参数尽量少,超多4个参数须要考虑采用结构体/类封装。ide
采用结构体/类封装的好处除了减小方法长度,更重要的意义在于将来版本接口功能升级只须要新增配置属性,而不用提供新的接口。函数
// bad case
void setMusic(String path,long startTime,long duration); // android; 使用music类封装
复制代码
能采用同步接口的尽可能不要用异步,异步接口须要在注释里说明。
设计同步接口的行为是明确的,设计异步接口可能让开发者误觉得接口行为已经完成,从而作出一些错误调用。异步接口必须强调说明,并注明对应的回调方法是什么。
// bad case
int finishRecording(); // android; 异步接口须要说明对应的回调方法
复制代码
一个接口只作一件事,使用doSomething
命名。
// bad case
int editCompleted(); // android; 改成stopEdit/finishEdit
复制代码
typedef NS_ENUM()
语法定义。// good case
typedef NS_ENUM(NSInteger, AVCaptureFlashMode) {
AVCaptureFlashModeOff = 0,
AVCaptureFlashModeOn = 1,
AVCaptureFlashModeAuto = 2,
} //iOS; 系统api命名
// bad case
typedef enum : NSUInteger {
DIRECTION_LEFT = 0,
DIRECTION_RIGHT = 1,
DIRECTION_TOP = 2,
DIRECTION_BOTTOM
} DIRECTION_TYPE; //iOS; 没有使用驼峰命名,枚举值命名不规范,未使用oc声明规范
复制代码
// good case
public enum FormatStyle {
FULL,
LONG,
MEDIUM,
SHORT;
} // android; 系统api命名方法
// bad case
public enum AlivcLogLevel {
AlivcLogLevelDebug(0),
AlivcLogLevelInfo(1),
AlivcLogLevelWarn(2),
AlivcLogLevelError(3),
AlivcLogLevelFatal(4);
} // android; 枚举值命名不规范
复制代码
iOS
只有在同时须要getter/setter方法的时候暴露属性,不然使用方法代替。
使用oc基本数据类型,不要使用c/c++的基本数据类型。
android
不要直接暴露属性,提供getter/setter方法。
Param类必须有构造器。
// bad case
public int videoWidth; // android; 提供getter/setter方法
@property (nonatomic, assign) int bitrate; // iOS; 使用NSInteger代替int
复制代码
获取已经存在的对象使用get
命名。
// bad case
AliyunICanvasController obtainCanvasController(Context var1, int var2, int var3); // android; 使用get代替obtain
复制代码
获取一个new的新对象使用create
命名。
create能够给开发者暗示资源的是被建立出来的。
// good case
public static AliyunIEditor createAliyunEditor(); // android; 工厂方法使用create建立新实例
// bad case
AnimPlayerView newPasterPlayer(); // android; 使用create代替new
复制代码
参数设置类/结构体命名须要声明用途,以Param
结尾。
后缀能够是Config,Setting等等,一旦肯定好须要全部接口保持统一。
// good case
public class AliyunVideoParam; // android; 命名符合规范
// bad case
public class CropParam; // android; 没有前缀
public class MediaInfo; // android; 没有声明是录制参数,没有以Param结尾
复制代码
设置效果:setXxx
,移除效果:clearXxx
// bad case
int addAnimationFilter(EffectFilter var1); // android; 不符合规范
- (void)deletePaster:(AliyunEffectPaster *)paster; // iOS; 不符合规范
复制代码
新增:addXxx
,删除:removeXxx
,清空:clearXxx
// good case
int addMediaClip(AliyunClip clip); // android; 替换为clearPartList
// bad case
void deletePart(); // android; 替换为removePart
void deleteAllPart(); // android; 替换为clearPartList
复制代码
数组/字典命名使用变量名+List/Map/Array/Dictionary
之因此不用复数形式定义变量名,主要考虑到咱们开发团队对名词的复数形式并无深刻掌握。与其命名不专业的变量名,不如用这种方式更为直观。
// good case
public List<Frame> getFrameList(); // android; 符合命名规范
// bad case
@property (nonatomic, copy) NSArray<AliyunFrameItem *> *frameItems; // iOS; 替换为frameItemArray
复制代码
回调方法除非特殊需求通常在主线程回调,回调接口注释必须说明在哪一个线程。
保证开发者的全部接口调用都在主线程执行能够避免不少线程问题。
有一些回调须要在特定线程如渲染回调等须要注释说明。
iOS
回调类统一以Delegate
结尾,会调方法以回调类名+Did开始,一个回调对应一个delegate属性。
android
回调类统一以Callback
结尾,会调方法以on+回调类名开始。
// bad case
@protocol AliyunIPlayerCallback
@end //iOS; 不能使用callback结尾
复制代码
主要模块必须经过init方法并使用Param
配置参数初始化。
统一的初始化方式能让开发者快速熟悉SDK调用方式。
调用关键方法时构造native资源,完成回调后内部析构native资源。
例如:调用transcode方法构造资源,transcode完成回调后析构资源。
对外提供成对方法处构造与析构native资源。
例如:startPreview与stopPreview,startEdit与stopEdit,open与close。
以上都不合适,考虑在dealloc
处内部析构native资源。
// bad case
- (void)destroyRecorder; // iOS; 考虑是否能够经过1,2,3条避免
复制代码
release
方法,release
删除底层资源与java同生命周期。// bad case
void dispose(); // android; 没有使用release()方法
复制代码
类,接口,结构体,枚举值必须加前缀。
java虽然有包名区分不一样类,但仍是建议加前缀,这样可让开发者更容易区分SDK接口。
安卓全部SDK接口必须在统一的包名内。
对外接口使用/** */
注释,不要使用//
注释。
不少接口须要多行注释才能解释清楚,因此在这里统一使用多行注释。
来源:本文为第三方转载,若有侵权请联系小编删除。