OpenHarmony API 设计规范
版本作者时间更新内容v0.1,试运行版OpenHarmony API SIG2022年11月初版发布 目的
API是软件实现者提供给利用者在编程界面上的定义,API在很大程度上体现了软件实体的能力范围。
同时,API定义的优劣极大影响了利用者的体验。
为了保障OpenHarmony生态健康发展,保证开发者利用体验,现制定OpenHarmony API设计规范。
范围
OpenHarmony API主要包含了对应用开放的外部API,以及体系实现部分的内部API。
本设计规范主要用以约束以下API(不区分编程语言):
- OpenHarmony Public API
- OpenHarmony System API
关于OpenHarmony API的分类,请参见《OpenHarmony API管理章程》。
接口设计目标
好的接口设计应该满意以下四点:
- 可利用 (Operational):这一点是毋庸置疑的,接口必须要能完成它提供的能力。可利用是最基本的要求。
- 富于体现力 (Expressive):与可利用同样告急的是,借助接口,利用者可以或许清晰表达他们想要做的事情。
- 简单 (Simple):接口的设计要保证学习和利用简单,避免难于理解大概利用堕落的情况发生。
- 可推测 (Predictable):API应当始终同等的按照接口的定义完成使命。假如接口定义中不包含堕落和异常的情况,那么这个API被调用任意多遍,都不应该发生任何异常。固然,假如现实情况是有可能堕落大概失败的,那就要在接口定义中说明清晰什么情况下会出什么错,并正确的在相应的机遇下返回对应的错误。
除此之外,API的设计还应该注意以下几点:
- API的稳定性和同等性是最告急的,API并非越多越好。
- API定名应当容易理解,API定名并非越短越好。
- API应当做好封装和抽象,并非暴露的信息大概能力越多越好。
从开发者利用API的阶段上来看,API应当做到:
API设计概述
为了使得规则尽可能通用,本规范不会涉及具体编程语言的差别,也不会涉及编码规约。这部分内容只要遵守相应的当地要求即可。
从整体上来看,本规范将API规范分成发布前评审规范和发布后评价两个部分。
- 发布前评审规范 :是对于API的最基本要求,所有API必须满意这些要求才能通过评审。
- 发布后评价 :只管在发布前已经做了很多的规则要求。但是仍然不能保证API是完美的。因此,API发布后仍然要保持对API的关注。发布后的审阅将影响对API提供者的评价。
以下是所有的API设计规则的摆列,以供快速索引,具体的说明在后文中展开。
每一个OpenHarmony API提供者都应该熟悉以下规则。
发布前评审规范
- 易用性
- 规则1:符合编码规则
- 规则2:正确利用英语语法
- 规则3:合理利用缩写
- 规则4:正确利用对仗词
- 规则5:正确利用设计/架构模式
- 规则6:避免有争议的定名
- 规则7:参数范例合理
- 规则8:参数数量合理
- 规则9:参数顺序合理
- 规则10:返回值定义完备
- 规则11:异常定义合理
- 规则12:从正面表达
- 规则13:低落堕落的可能性
- 规则14:避免顺序耦合
- 规则15:正确表达所有逻辑
- 规则16:注意封装
- 规则17:单一职责
- 可用性
- 规则18:保证可靠性
- 规则19:功能完备
- 规则20:考虑权限与隐私保护
- 规则21:考虑并发环境
- 规则22:注意资源管理闭合
- 规则23:考虑重试逻辑
- 规则24:满意幂等要求
- 规则25:考虑装备通用性
- 同等性
- 规则26:保证术语、概念同等性
- 规则27:保证装备间行为同等性
- 规则28:注意版本演进同等性
- 规则29:定名风格保持同等
- 规则30:参数顺序保持同等
- 规则31:同步/异步风格同等
- 兼容性
- 规则32:注意新旧体系版本的兼容性
- 规则33:二进制向后兼容性
- API资料
- 规则34:模块、定名空间、类必要包含底子说明
- 规则35:利用场景说明清晰
- 规则36:接口说明正确
- 规则37:参数说明正确
- 规则38:返回值/异常形貌正确
- 规则39:元信息完备
- 规则40:风格同等
- 组织方式
- 规则41:分层合理
- 规则42:模块划分合理
- 规则43:应用形貌文件也是接口的一部分
- 质量属性
- 规则44:性能满意要求
- 规则45:功耗设计合理
- 规则46:必要保证可测试性
- 规则47:考虑环境顺应性
发布后评价
- 稳定性
- 安全性
- 规则49:避免接口被滥用
- 规则50:避免接口被利用
- 可维护性
- 规则51:能承载业务演进
- 规则52:功能扩展后不影响原先行为
- 规则53:文档和资料配套更新
- 不可替换性
- 开发者反馈
发布前评审规范说明
易用性
任何设计都应该考虑易用性。对于OpenHarmony API来说,易用性必要考虑以下几个方面:
定名
API的定义首先应当满意所属项目的编码规约,例如:大小写的定义、下划线、连字符规则、前缀规则等。
对于OpenHarmony生态而言,可参考如下编码规约:
- 《OpenHarmony JavaScript语言通用编程规范》
- 《OpenHarmony C语言编程规范》
- 《OpenHarmony C++语言编程规范》
- 《OpenHarmony C&C++ 安全编程指南》
- 《OpenHarmony HDF驱动编程规范》
- 规则2:正确利用英语语法
API定名只允许利用英语。通常,类的名称是名词,例如:XXXManager,XXXService,XXXAnimation等。函数通常是动词或动宾布局,例如:start(),createUser(),startBoot()等。
对于一个名称叫做Start的类,大概一个叫做ball()的函数,通常是错误的做法。
另外,还请注意及物动词和不及物动词,避免语法错误。对于动词,根据场景必要注意时态。例如:假如机遇上就存在“开始传输”和“渲染完成”这些事件,就应该用上transferStarted()以及renderDone()这类的表达。
应当尽可能避免利用缩写。缩写不仅仅是难于理解,另有一个问题是:一个缩写可能会有多个解释,在上下文信息不全的情况下,利用者会很难分辨。
假如是大家都熟知的缩写是允许的,除此之外,应该只管避免。尤其是,只有自己能明白的缩写,这是禁止的。
在语言表达中,同一个含义可能有很多个类似的词可以表达,例如:
词语近义词senddeliver, dispatch, announce, distribute, routefindsearch, extract, locate, recoverstartlaunch, create, begin, openmakecreate, set up, build, generate, compose, add, new 但在API定名中,应当注意对仗词的利用。
例如,假如你利用了add就应该利用remove,而不是destroy。利用了increase就应该利用decrease,而不是reduce。
下面是一些常见的对仗词:
词语对仗addremoveincreasedecreaseopenclosebeginendinsertdeleteshowhidecreatedestroylockunlocksourcetargetfirstlastminmaxstartstopgetsetnextpreviousupdownnewold
设计模式大概架构模式其实是软件行业的“行话”。既然是行话,就要正确的利用,否则别人就可能误解你的意思。
因此,当你的接口中包含了Strategy,Builder,Factory,Singleton这类的词语时,请确保你正确理解了这些设计模式,并且正确利用了它们。
另外,假如你确定的是在利用某个模式,那就正确的在名称上利用标准术语,不要任意修改词性大概打乱名称的词语顺序,如许利用者会更容易理解。
定名要遵守的底线,是应该避免有争议的名称。这其中,包括但不限于任何违反法律、产生宗教争议、或导致种族歧视的词语。
固然,利用脏话也是绝对禁止的。考虑到OpenHarmony会使能千行百业,对于避免有争议定名,必要思考得更多。
参数
通常,利用类范例参数比利用简单范例要更好。
例如:对于下面的一组接口看似没什么问题:
- addPerson(string id, string name, int age)
- removePerson(string id, string name, int age)
- modifyPerson(string id, string name, int age)
但是这个定义不便于扩展,由于后期可能要新增参数。但是假如一开始就通过一个类范例Person来定义参数,则添加一个字段就很容易,并且,对于接口本身不消做任何改变。
利用类范例而不是简单范例,另有一个利益是:参数数量会少,更容易记忆。
参数数量应当控制在7个以内。在更多的情况下,参数控制在3~5会更容易利用。
在任何情况,参数的数量都不应当高出10个,这种接口通常很难记忆和利用。假如遇到这种情况,通常是对范例没有做很好的封装,大概是接口的实现逻辑包含太多的内容。请考虑拆解。
在一些编程语言中,常常会以先输入参数、后输出参数的顺序来组织参数列表。
但其实,假如能再根据参数的大小逻辑关系、参数的告急性来进行排序,那对于利用者来说,会更容易记忆和利用。
好比,可选参数应该放到必选参数的后面,回调函数作为参数应该放到最后等。以fs.readFile 方法为例,路径参数是必填的,encoding 和 flag 是有默认值的。
- fs.readFile(path[, options], callback)
复制代码- fs.readFile('/etc/passwd', (err, data) => {
- if (err) throw err;
- console.log(data);
- });
- fs.readFile('/etc/passwd', {
- encoding: 'utf-8',
- flag: 'r+'
- }, (err, data) => {
- if (err) throw err;
- console.log(data);
- });
复制代码 返回值
返回值定义完备必要做到:不能只考虑正常情况,对于接口的定义,也要考虑异常和无效的情况。要让开发者能合理处理。
例如:
- 对于数字范例的返回值,要定义清晰返回的范围,什么情况下会出现极值。
- 对于布尔范例的返回值,要定义清晰什么时候返回true,什么时候返回false。
- 对于数组大概集合范例的返回值,要定义清晰什么时候返回null,什么时候返回包含空元素的集合。
- 对于罗列范例的返回值,要确保每种情况都正确定义了。
- 规则11:异常定义合理
异常是特别情况的返回,这通常意味着输入参数无效大概函数根本无法正常处理。
对于同一个模块,大概同样的业务,在何种情况下返回错误值,在何种情况下直接返回异常,应该有统一的定义。
另外,针对同样的异常情况下,应当返回同样的异常范例。
保持同等可以很大程度上低落开发者的利用难度,并且避免利用堕落。
其他
对于易用性而言,除了上面提到的内容,另有一些推荐的做法,可以低落开发者的利用难度。
从正面表达可以低落开发者的思考资源。
平凡来说就是:接口的定名只管利用肯定的词语,而不是否定方式的表达。由于否定表达很难理解。
下面是一个反例:
- if (!isNotAccessible() || !isNotWritable() || !isNotPrintable())
复制代码 如何接口都是正面表达,就更容易理解了,下面是推荐的做法:
- if (isAccessible() && isWritable() && isPrintable())
复制代码
调用者利用错误很大程度在于参数传递上。
例如下面这个函数。第二个和第三个参数都是布尔值,如许利用者很容易记错顺序,大概搞不清晰该传入true还是false。
- declare function findString(text: string, isForward: boolean, isCaseSensitive: boolean): string;
复制代码 通过罗列在定义上就避免这种错误存在的可能:
- enum SearchDirection {
- FORWARD,
- BACKWARD
- };
- enum CaseSensitivity {
- SENSITIVE,
- INSENSITIVE
- };
- declare function findString(text: string, direction: SearchDirection, sensitivity: CaseSensitivity): string;
复制代码 很多时候,通过罗列代替布尔值以及整数表达的范例,可以淘汰利用者堕落的可能性。
再者,通过将多个参数构成一个范例的情势,也可以低落参数传递堕落的可能性。
软件设计要尽可能保证高内聚,低耦合。这一点对于大型项目尤其告急。
耦合表达了软件模块之间的依赖程度,耦合过深常常意味着架构设计没有做好。
从一个软件静态布局大概分层架构图上可以看得出:假如模块之间的依赖关系比力少,上下层之间有显着的调用方向,则是比力好的设计。相反的,假如模块之间依赖关系非常复杂,大概上下层之间存在相反方向的调用链,则不是一个好的布局。
耦合有很多种,对于接口来说,顺序耦合就是要注意的。顺序耦合是指:类中方法必要按照某个特定的顺序调用才能工作。
类似下面这组定名的接口,很可能就是顺序耦合:
- doSomethingFirst()
- doSomethingSecond()
- doSomethingThird()
复制代码 这种设计的问题在于:对于利用者的要求太高了,很容易用错。
对于这类问题,通常可以利用模板方法设计模式来办理。
有些接口是用来查询信息的,例如getUserAccount()。有些是进行操作的,会修改数据,例如createUserAccount()。
永久不要在一个看起来是查询操作的接口内里去做修改数据的动作 ,由于如许利用者会非常的迷惑。
更通用的来讲, 接口的名称应当表达它所做的所有事情 ,不要有所遮掩。只有如许,在阅读代码的时候,才能更容易理解代码到底做了什么。
假如接口包含了内容太多,很难通过几个单词形貌清晰所做的所有事情该怎么办?对于这种情况,通常意味着必要将这个接口拆分成多个,由于这个接口不够“内聚”。
另有一些情况下,大家在做某件事的时候,会本能的以为别人也有同样的背景认识。但毕竟往往并不是如许。在编程时也是一样。
例如,对于一个形貌编程语言的字段,可能会将其定名为language。这是由于大家默认认为已经在讨论编程语言了,但是language到底是编程语言还是国际化语言?是不是叫做programmingLanguage更好一些呢?
固然,对于这一条还是要举一个反例,不要走到另外一个极端:假如类名大概namespace名称中已经明确带了一个前缀,在函数中就没须要再重复一遍了。毫无信息量的冗余是没有须要的。
对于面向对象编程来说,大家都知道“封装”的告急性。
这意味着,体系能力应当尽可能封装好实现细节,提供简便的接口供调用者利用。
这就好像冰山,埋在水里的部分不管多庞大,露在水面上的部分要足够的小,足够的简便。这才是友好的界面(Interface)。
封装的告急性,不仅仅是让开发者容易利用。其实也是使得开发者不容易堕落。举一个生活中的例子:电子工程师将房间的电门路接好之后,只留一个开关按钮给到用户,这就是一个很好的封装。既避免了让用户理解复杂的电路布局(方便利用),也不会产生触电(堕落)的风险。
一个 API 应当只管只做一件事情,尽可能保持单一焦点的职责。例如:
- // 不推荐
- view.fetchDataAndRender(url, templete);
- // 推荐
- let data = view.dataManager.fetchData(url);
- view.render(data, templete);
复制代码 这么做的目的是由于:开发者可以根据必要,按最小单元来利用接口。也可以根据多个单一职责的组合,来进一步封装自己必要的业务逻辑。
单一职责与注重封装性在一些情况下可能是抵牾的:假如提供了多个单一能力的接口,势必须要利用者关心多次的调用细节。
在这种情况下,必要从封装的“层次”来考虑决定,接口提供给开发者的,到底是哪个层级的能力?面向高层次利用者的一件事,对于低一个层次来说,可能就是多件事情。
可用性
操作体系所提供的接口,必须是完全可靠的。
固然,可靠性不意味每次调用都是乐成的。例如:在资源已经耗尽的情况下,应当给调用者合理的返回值大概异常。
每个接口必须针对所有的情况进行正确的定义,并在相应的情况下按照定义的行为完成工作。
没有按照预定行为返回结果,大概无故导致应用步伐异常都属于不可靠行为。
每个特性大概能力在规划时,需考虑到功能的完备性。不应当出现:在支持的范围内,某个流程中断,大概某个选项缺失的情况发生。API设计者应当做好足够的验证和推演。
即便操作体系的特性常常会伴随体系版本几年内才能完全迭代完备。但是,对于每一个特定版本,在其包含的范围内应当是闭合的,自洽的。这要求模块的设计者划分好版本迭代的边界。
任何涉及到用户数据、用户隐私的接口都必须做好权限限定和数据保护。
对于权限控制,应当依照以下几个原则:
- 完备性原则:统统穿透应用沙箱的行为都需考虑利用权限来管控。
- 最优粒度原则:一个权限只保护一类对象;一个接口只需申请最小粒度的权限。
- 清晰完整原则:权限定义中必须清晰说明保护对象、开放范围、敏感级别。
- 最小开放原则:一个权限仅对确有正当业务需求的应用开放,开放控制可通过权限来实现。
对于隐私包含,应当遵守以下几个原则:
- API调用的返回仅包含须要的内容, 避免携带额外信息。
- API调用不允许获取、收集用户个人数据, 除非通过用户权限管控、由用户授权同意。
- API涉及跨应用调用时,如涉及个人数据向被调用者的披露,由调用方在隐私声明中说明披露的数据范例、数据接收者和数据利用目的。
- API涉及到用户敏感数据(如电话、通讯录、媒体等)访问时,必要利用system picker的机制,禁止API通过申请敏感权限方式访问。
- API开放禁止捆绑与所开放能力不相关的功能。
保证所有的接口线程安全必要付出很大的代价(步伐复杂度、性能影响),因此OpenHarmony API不须要求所有接口线程安全。只需根据必要选择即可。
但是,对于设计上就是为了并发环境利用的接口,必须做好相应的设计和说明。
固然,对于接口的内部实现中,应当尽可能避免出现线程不安全的问题发生。
在一些情况下,有些接口包含了动态资源的申请。此种情况下,这些接口也必要考虑到资源的释放。
假如申请的资源是直接返回开发者的,则必要提供相应的接口来释放资源。
假如资源没有直接返回给开发者,则接口自身要考虑到资源的生命周期,以及释放的机遇。
对于有资源利用上限的情况,应当给开发者说明清晰。并且提供接口查询是否已经到达上限。
对于独占资源更加应当注意资源的释放机遇。
对于可能失败的接口,应当考虑好给开发者相应的机制来重试。例如:对于相机这类独占的资源,一旦有某个进程利用了,其他进程就无法获取到。这种情况下,应当提供接口给开发者查询是否可用的接口。
在一些情况下,为了避免开发者反复实验,还必要提供给开发者状态监听的机制。
并且,API要明确客户端调用失败后,可以或许发起重试的最大次数。
在数学中,幂等用函数表达式就是:f(x) = f(f(x))。例如求绝对值的函数,就是幂等的,abs(x) = abs(abs(x))。
盘算机科学中,幂等表示一次和多次请求某一个资源应该具有同样的结果,大概说,多次请求所产生的影响与一次请求实验的影响结果相同。
具体到现实场景:文件打开,大概的硬件资源利用,打开一次和打开多次其结果应当是一样的,不应该有其他副作用。固然,关闭,也是类似。
OpenHarmony是为多种不同范例装备设计的统一操作体系。
考虑到装备的复杂性,接口的设计应当要能面临多种装备的实用性。例如:
- 对于用户界面的控件,要考虑到不同屏幕尺寸的大小。
- 对于数据的存储,要考虑到不同大小的存储空间。
- 对于用户输入事件,要考虑不同的用户交互方式:触摸、语音、按键等。
固然,有一些API只在某些特定装备上才有,例如:
- 健康类传感器只在穿戴装备上有
- 车控类接口只在车机装备上有
这种情况,请参考《SysCap利用指南》,来标定API的实用范围。
同等性
为了容易理解,接口在定名和说明上应当注意术语和概念的同等性,不应该出现“方言”。基于场景的业务模型抽象,形成OpenHarmony的连贯、同等、自恰的业务概念。
在这方面,应当依照以下原则:
- 每个概念只应该有 唯一 的一个术语,同一个对象不应该有两个名称。
- 术语定名应该是 贴切的 , 可解释 , 易理解 。
- 术语的定义应当 精准、无二义。
- 对于业界通用术语, 不应该重新定义 ,应当依照业内做法。
总体来说,要避免随意添加术语概念,尽可能以《OpenHarmony 技术术语集》为准。假如必要,可以在术语集中新增词条。
在默认情况下,同一个接口在不同的装备上应当保证行为是同等的。
对于由于装备形态而导致的行为不同等,应当给予开发者充分的说明,并且提供相应的查抄机制。
在默认情况下,所有接口应当保证在体系版本演进的情况下,其行为是同等的。假如在新版本上出现了行为不兼容的变更,最低要求是必要对应用的目标版本做区分处理。
简单而言:接口的行为变更不能影响已经开发完的应用。
在一些 API 设计的场景中,即使定名已经做到正确,但在有些情况下仍然可能碰到不同等的场景。好比,API 中常常会看到 picture 和 image、path 和 url 混用的情况,这两组词的意思非常接近,容易出现杂乱。在指代同一内容时,应该利用相同的定名。
例如,下面这些接口都是为了获取媒体资源,但是定名风格不同等。
- declare function getMediaAsserts(): Array<MediaAssert>;
- declare function getAudios(): Array<AudioAssert>;
- declare function getVideos(): Array<VideoAssert>;
- declare function getImages(): Array<ImageAssert>;
复制代码 应该修改为:
- function getMediaAsserts(): Array<MediaAssert>;
- function getAudioAsserts(): Array<AudioAssert>;
- function getVideoAsserts(): Array<VideoAssert>;
- function getImageAsserts(): Array<ImageAssert>;
复制代码
为了开发者便于理解,对于同一个定名空间大概是同一个模块来说,其成套的接口的参数顺序应当是同等的。
例如:deviceId和missionId在单个接口中的顺序没有强制要求,但是在同一模块接口中必须保持顺序同等。
- function getMissionInfo(deviceId: string, missionId: number): Promise<MissionInfo>;
- function getMissionSnapShot(deviceId: string, missionId: number): Promise<MissionSnapshot>;
- // 正确
- function getLowResolutionMissionSnapShot(deviceId: string, missionId: number): Promise<MissionSnapshot>;
- // 错误
- function getLowResolutionMissionSnapShot(missionId: number, deviceId: string): Promise<MissionSnapshot>;
复制代码
异步接口应该可以通过入参和返回值判断出来,风格上要保持统一。例如:
- // Callback形式
- function getDefaultDisplay(callback: AsyncCallback<Display>): void;
- // Promise形式
- function getDefaultDisplay(): Promise<Display>;
复制代码 假如同时提供了同步和异步接口,可以在同步函数名后加上后缀Sync加以区别,例如:
- function getDefaultDisplaySync(): Display;
复制代码 假如只提供了同步接口,且返回值不是void,可以不消加后缀,例如:
- function registerMissionListener(listener: MissionListener): number;
复制代码 兼容性
API 变更的资源非常高,在设计之初就应该做尽可能完备的考虑。但是API变更始终是体系发展过程中不可避免的。
API 变更要保证向后兼容原则,API 变更后,废弃的 API 要在源码和文档中显著标识 deprecated,并引导开发者利用变更后 API 。
废弃的 API 要保证其功能仍然可用,至少生存5个版本。5个版本后,在充分告知开发者,并给与充分的时间供开发者修改后,可予以删除。
二进制兼容,指版本演进后,开发者已有步伐不消重新编译可正常链接、运行。这也意味着,二进制兼容是保证在版本升级的情况下,对象实例的内存布局不会发生变化。
以C++接口为例,常见粉碎二进制兼容的API变更包括:
- 任何API元素删除
- 添加新的虚函数
- 改变类的继续
- 改变虚函数声明时的顺序
- 添加新的非静态成员变量
- 改变非静态成员变量的声明顺序
由于C接口相比C++接口在二进制兼容性上有天然的上风,所以OpenHarmony的Native API推荐利用C接口定义。
API资料
API的很多信息必要通过文档和资料进行说明,因此,配套的这些内容也应当做好质量管理。
- 规则34:模块、定名空间、类、函数必要包含底子说明
每个模块、定名空间、类和函数都必要包含基本的说明。
对于关键模块、复杂模块必要有具体的说明。
说明采取英文的情势。
所有接口应当提供示例代码,覆盖常见利用场景。
复杂接口必要具体说明利用场景。可以在接口的说明上增加具体教程的链接。
接口说明的笔墨不应该出现拼写错误大概错别字。
所有代码示例应当保证可以或许正常运行。假如接口在不同版本上存在行为不同等,则必要针对每种情况做具体说明。
对于废弃接口,需做明确标记,并做好替换接口说明。
对于API的每一个参数,都应该说明清晰。例如:
- 假如是非简单范例,则需说明清晰是否允许参数为空
- 假如是罗列范例,则需针对每个罗列值说明清晰利用场景
- 假如是可选参数,则需说明清晰什么时候该传参,什么时候可省略
规则38:返回值/异常定义正确
假如接口有返回值/异常,必要在接口说明中形貌完整。例如:
- /**
- * Sync function of rename.
- * @param {string} path - path.
- * @returns {void} rmdir success.
- * @throws {BusinessError} 401 - if type of path is not string.
- * @throws {BusinessError} 201 - if permission denied.
- * @syscap SystemCapability.FileManagement.File.FileIO
- * @since 7
- */
- declare function rmdirSync(path: string): void;
复制代码
接口的方法说明上应当包含基本的元信息,例如:@syscap、@since等。
元信息形貌了API自身的基本信息。工具和SDK会利用这个信息进行相应的处理,例如:针对已经废弃的接口会给出告诫提示。
API的说明资料,在风格和样式上应当保持同等。例如,笔墨的加粗,资料图片的配色等。
组织方式
操作体系通常是以分层的模型来进行架构的。这意味着每一层要办理不同层次上的问题。
因此,接口的设计也要注意相应的层次。
可以利用建筑行业来进行类比:所有的建筑都会包含墙体和房间的门。墙体是有砖块构成的,门是有木料构成的。在这种情况下,抽象可能会分为下面几层:
- 第一层是基本都原质料,包括水泥、沙子、木材等
- 第二层是用原质料构建出来的建筑元素,例如:门、窗户、墙体等
- 第三层是房间的种类,例如:卧室、卫生间、客厅等
- 最后,最上面一层是各种不同用途的建筑。例如:酒店、公寓等
这里每一层所要考虑的概念和要处理的问题都是不一样的,请仔细思考你提供的API是在哪一个层次上。
大家不仅要关注水平层面的分层,也应该关注垂直层面的划分。因此,模块的组织也同样告急。OpenHarmony的接口是以定名空间的情势组织的。一个定名空间通常对应了一个模块。
从子体系的角度,一个较小的子体系应该尽可能将接口放在同一个定名空间中。较大的子体系,可以提供多个定名空间。
接口的英文是Interface,这个词另有界面的意思。操作体系提供给开发者的接口并不仅仅是编程接口。任何公开给开发者的机制,都是API的一部分。
这其中,最显着的就是应用形貌文件,这些文件也必要按照接口规范一样的规则来管理。
质量属性
操作体系所提供的接口所有上层应用都可能会利用到,因此,其实现的性能应当是可以或许满意现实要求的。
下面是一些举例:
- 应实时相应,避免调用者等待;假如API调用实验时间过长应设计为异步方式。
- 接口存在大量数据传输时,应考虑利用共享内存、消息队列等。
- 尽可能淘汰新增长程实体。
- 对利用资源的API调用必要能实时释放资源,异常场景具备容错机制,保障资源实时释放。
OpenHarmony 操作体系在设计之初就必要面向多种不同范例的装备,这些装备中绝大多数是无源装备。因此,功耗的考虑是毋庸置疑的。
每个功能/机制在实现上应当把功耗的合理性作为最基本的要求。除此之外,对于高功耗的接口应当在说明中给开发者充足的解释,并且对于利用方式给与足够的指导。
同时,在利用上要避免开发者错误的利用。例如,在装备锁屏之后,大概应用切换到背景之后,就应当停止高功耗的行为。
另外,还应当注意在版本演进过程中,功耗不出现劣化。
完备的自动化测试用例,可以带来很多利益:
- 开发过程中,提前快速发现问题,进步接口质量。
- 版本迭代时,保证代码修改不影响功能,进步代码修改的信心。
- 确保接口向后兼容性。
所以,对于一个OpenHarmony API,都要求做到以下几点:
- 新增API必须同步交付API自动化测试用例,用例100%覆盖API接口。
- 用例场景单一,单条用例覆盖接口单个功能场景,简化单条用例代码逻辑。
- 用例实验高效,每条用例实验时间控制在毫秒级。
- 用例实验全自动化:接口用例必要达成100%自动化。
- 用例有效性:用户要求必须存在断言,且不能仅是查抄是否抛出异常,必要有功能逻辑的断言。
考虑到用户的个性化,操作体系通常会提供一些环境自定义的能力,例如:语言国际化、浅色/深色主题、字体大小等。
对于这方面相关的接口,在开发者没有传递指定参数的情况下,应当可以或许根据当前所属的环境,自顺应的返回相应环境下的结果。
发布后评价说明
只管在API发布前已经做了多方面规则约束,但总可能另有一些问题在开发者利用之后才能发现。
即便是如许,但并不意味着在设计API之初不必要考虑这些问题。
相反的,大家应当时候避免发布后问题的发生。
假如在接口发布后,发现了下面几条规则问题的发生,则该模块的接口质量是比力差的。
稳定性
对于接口来说,稳定是其最告急的属性。
这是由于接口的废弃和行为变更将极大的影响开发者的维护效率。考虑上多种装备范例、多个体系版本,这个问题会变得更加复杂。
设计出能保证长期稳定,一连兼容的接口是每个API设计者应当寻求的目标。
接口的废弃率/变更率与接口的质量在一定程度上成反比。
安全性
所有接口在设计上,应当考虑到不能被过度滥用。滥用既可能是数量上的滥用,也可能是范围上的滥用。
例如:对于用户公共数据(相册、联系人等)的访问,对于长时间背景运行的能力,都可能被滥用。
对于数量滥用的防止,可以考虑“仅一次授权”如许的机制。
在实现上,可以根据调用者的身份进行相应的限定。
固然,无论是哪种限定,都应该在接口说明中说明清晰。并且在检测到过度滥用的情况下,有相应的返回值告知调用者。
被滥用是指接口的利用超出了原先预期的限定。而被利用是指:接口可以被用来造成负面影响,例如攻击体系。
无论开发者以何种方式利用OpenHarmony提供的接口,假如某个接口,或某些接口组合调用导致体系出现了问题,那么就一定接口本身存在问题。
特别的,假如某些接口(无论在何种情况下)一旦调用就可以利用体系瓦解,大概进入不能工作的场景,大概可以或许窃取用户数据,这是绝对不允许的。这就要求API的设计者从API被调用的所有可能都场景上进行考虑,避免一些极端情况的发生。
可维护性
考虑到操作体系的一些较大特性,必要颠末数年才能打磨完成。因此接口在设计上应当考虑以后的可扩展性。
随着能力的演进,假如出现了在新版本上新起了一组新的接口,而废弃了原先旧的接口,那么就是接口的设计上存在问题。这是应当避免的。
随着OpenHarmony 操作体系版本的演进,一些接口新增了参数,大概一些参数新增了新的选项是很常见的一种行为。
但是,在这种情况下,一定要考虑清晰对于过往已存在行为的同等性。不应当由于新的场景的引入,而粉碎了原先存在的行为。
这里必要依照的原则是:假如在新版本上行为要发生变化,只允许针对目标版本是新版本的应用见效。对于目标版本是旧版本的应用应当继续保持原先的行为。
当大家在更新接口实现的时候,一定要记得更新接口的说明。从兼容性的角度来看,不能修改原先存在的行为。通常应当提供新的接口来完成新的功能。
但在下面的情况下,可以考虑修改既有接口的行为:
- 缺陷的修复。
- 性能/功耗的改进。
- 为接口拓展新的特性或场景,但不影响原有特性或场景逻辑。
第1、2种情况,通常在Release Note内里说明;第3种情况就必要在接口说明上表述清晰。
不可替换性
正交(Orthogonality),意味着多个接口之间不应该出现重合的功能。
例如:一个接口提供了创建用户账号的能力。另外一个接口包含了创建用户并登录的功能。假如是如许,就应该把第二个接口拆开,只生存单独登录的功能。
假如将接口的能力画成一个个图形,那么这些图形之间不应该出现重叠交错的想象。对于同一个层级的接口,不应该出现某个接口提供了1、2、3功能,另外一个接口又提供了2、3、4功能这种情况发生。
固然,为了便于调用,允许将一些接口组合成一个更高阶的接口。这通常是抽象层次的不一样,并非是同一个层次的重叠。
开发者反馈
只管API在正式发布之前,会颠末试用阶段,但考虑到试用时间和试用范围是有限的,现实上无法保证能避免所有问题。
因此,在接口正式发布之后,也应该继续关注开发者的反馈。
开发者反馈可能分为以下几种情况:
- 盼望提供更多API来满意目前不能实现的功能,这种情况可以将需求导入到下个版本的规划中。
- 反馈API的行为与文档形貌不同等,这通常是缺陷,应当尽快修复。假如是文档的问题,也应当尽快修改。
- 反馈接口设计不合理,可能是定名大概参数设置存在问题,这种情况就必要考虑API废弃同时用新API代替。
在一些情况下,可能要变更已经发布的API行为。这种情况下,应当要注意:行为变化只能发生在新开发的应用上,对于已经发布的应用,不能产生行为变化。API提供者可以根据应用的目标API版本来进行判断。
在最坏的情况下,必要将既存API废弃,提供新的API代替。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |