当你设计新的API时你需要做很多决策。一般都是基于大量的设计原则来做出的这些决策。Joshua Bloch在他的
报告中总结了一些。他提到的主要原则有以下这些:
易于学习
易于使用
不易误用
写出的代码易于阅读及维护
足够强大,能满足需求
易于扩展
对使用者友好
从上面这个列表可以看到,Joshua Bloch强调的是可读性和易用性。但这个列表中完全忽略了的一点就是性能。不过性能会影响到你的设计 吗?
要回答这个问题,我们先来设计一个API的简单的用例,然后再测试它的性能。这样我们可以根据结果,来看下API的设计是否会对性能产生影响。作为例子,我们使用的是一个经典的从服务或者存储中加载一个客户列表的用例。我们需要考虑的是并不是所有的用户都能执行这个操作。因此,我们必须实现某种权限检查。要实现这个检查并将信息返回给调用方,我们有好几种实现的方式 。我们首先来尝试下这个:
List<Customer> loadCustomersWithException() throws PermissionDeniedException
这里如果调用方没有权限来获取客户列表的话,我们给它定义了一个显式的异常。方法返回的是一个客户的列表,由于我们假设可以从某个容器或者ThreadLocal的实现中获取到用户,因此在没有在方法参数中传入用户信息。
上述方法的签名很方便使用,同时也很难用错。使用了这个方法的代码看起来会是这样的:
try {
List<Customer> customerList = api.loadCustomersWithException();
doSomething(customerList);
} catch (PermissionDeniedException e) {
handleException();
}
读者很快就会发现,只有当我们没有出现PermissionDeniedException异常的时候,才会返回一个客户列表,我们才会执行后续的操作。不过从影响性能的角度考虑的话,异常的确会消耗一些 CPU时间,因为JVM必须中断正常的代码执行,同时去遍历栈来找出继续执行的位置。如果我们考虑到现代处理器架构的话这个会更加麻烦,因为它们希望能在管道中来执行代码序列。因此出于性能的考虑,使用另一种方式来通知调用方没有权限的话会不会更好一些呢?
第一个想法就是创建另一个方法,在调用这个最终会抛出异常的方法前来检查权限。调用的代码看真起来会是这样的:
if(api.hasPermissionToLoadCustomers()) {
List<Customer> customerList = api.loadCustomers();
doSomething(customerList);
}
这段代码的可读性也不错,不过我们引入了另一个方法调用,它会消耗一定的开销。不过现在可以确定的是不会抛出异常了,因此我们可以省掉这个try/catch块了。但这段代码破坏了易用性的原则,因为现在在一个用例中,我们需要调用两个方法,而不是一个。你要注意别忘了每次获取操作中还有一次额外的方法调用。考虑到整个项目,你的代码到处可能都是这些检查权限的语句。
还有一个解决异常的方法就是提供一个空列表给这个API,让具体实现去填充它。返回值可以是一个布尔值,来表明这个用户是否有权限来执行这个操作,或者来表明如果这个列表是空的,只是因为没有查找到用户。这听起来像C或者C++编程的方式,调用方来进行内存管理,被调用方来使用它。这个方法的话就算你没有权限获取列表,也会有一个空列表的开销,:
List<Customer> customerList = new ArrayList<Customer>();
boolean hasPermission = api.loadCustomersWithListAsParameter(customerList);
if(hasPermission) {
doSomething(customerList);
}
解决这个问题的最后一个方法是返回两段信息给调用方,这需要增加一个新的类,它不仅持有用户列表,还有一个布尔值,来表示用户是否有权限来执行这个操作:
CustomerList customerList = api.loadCustomersWithReturnClass();
if(customerList.isUserHadPermission()) {
doSomething(customerList.getCustomerList());
}
由于我们得创建额外的对象,这样消耗了内存和性能,我们还得处理这个额外的类,而这个类什么也不干 ,只是作为一个简单的数据容器,来提供两段信息而已。尽管这个方法也很容易使用,写出的代码可读性也很强,但是它为了维护这个单独的类,带来了额外的开销,还用了一种很无语的方式来表明返回的这个空列表之所以是空的是因为没有权限。
介绍完这三种不同的方法后,现在该来测试下它们的性能了,一种情况是调用方有权限,另一种是调用方没有权限。下表中的结果是第一种情况执行了1000000次后的结果:
Measurement | Time[ms] |
testLoadCustomersWithExceptionWithPermission | 33 |
testLoadCustomersWithExceptionAndCheckWithPermission | 34 |
testLoadCustomersWithReturnClassWithPermission | 41 |
testLoadCustomersWithListAsParameterWithPermission | 66 |
正如我们所预想的,使用了额外的类来以及传入空列表的的这两个方法和抛异常的实现相比开销要更大。即使是使用了一个专门的方法来检查权限的这种方式也没比直接调用慢多少。
下表是调用方没有权限来获取列表时的结果:
Measurement | Time[ms] |
testLoadCustomersWithExceptionNoPermission | 1187 |
testLoadCustomersWithExceptionAndCheckNoPermission | 5 |
testLoadCustomersWithReturnClassNoPermission | 4 |
testLoadCustomersWithListAsParameterNoPermission | 5 |
这个抛出了特定异常的方法比其他方法要慢得多。它影响的量级比你原先想的要高得多。不过从上表中我们也知道这种情况的解决方案了:如果没有权限的情况非常多的话,增加另一个方法来进行权限检查。有没有权限时运行时差异这么大是因为,在这里如果有权限的话我返回的是一个只有一个客户的ArrayList,因此没有权限的话loadCustomer()方法的调用的开销是要昂贵一点。
结论
当性能是一个关键因素的时候,你在设计新的API时也应该考虑到它。从我们上面看到的性能分析来看,最后选择的解决方案可能会破坏了API设计中的易于使用和难以用错的原则。
原创文章转载请注明出处:
http://it.deepinmind.com
英文原文链接
分享到:
相关推荐
ThinkPHP5是一个专为API开发设计的高性能PHP框架,它在保留快速开发和简洁易用的核心理念的同时,对技术进行了全面升级。这个框架的出现,旨在提高开发效率,提供更稳定、安全和高效的解决方案,尤其适用于构建...
### 各种API性能与性能优化技巧 #### 性能计数单位 在讨论API性能时,首先要理解时间单位的概念,这有助于我们更好地衡量不同操作的速度。常用的性能计数单位包括: - **秒(sec)**: 最基本的时间单位,1秒等于1,000...
ThinkPHP 是一个流行的开源 PHP 框架,专为 API 开发设计,具有高性能和易用性的特点。在 PHP8.0 的环境下重构,ThinkPHP 针对现代 Web 应用开发的需求进行了优化,提供了丰富的功能和工具,极大地提高了开发效率。 ...
RESTful API设计:API性能优化与缓存策略.docx
API设计不仅是技术实现的问题,更是人与人之间沟通的桥梁。本文将探讨API设计的一些核心准则,以帮助创建出易于理解、维护和扩展的接口。 首先,提供清晰的思维模型是API设计的基础。API设计者需要建立一个直观、...
由于其简洁的API设计和广泛的.NET支持,WebAPI在Web服务开发中非常流行。 3. 性能测试指标 性能测试通常关注以下几个关键指标: - 响应时间:服务处理请求并返回结果所需的时间。 - 吞吐量:系统在单位时间内处理...
《ASP.NET+Web+API设计》是美国作者布洛克撰写的一本深度探讨ASP.NET Web API开发的专业书籍。这本书详尽地介绍了如何利用ASP.NET框架构建高效、可扩展且易于维护的Web服务。Web API是微软为.NET Framework提供的一...
大多数人的经验都源于不断犯错或借鉴别人的经验,而本书试图打破这种局面,提炼出耐用且不会过时的API设计策略。API有哪些品质要求,有哪些关键要素,有哪些通用的技巧,设计时要遵循哪些原则……这些问题都能在本书...
**API网关设计和实现** API(应用程序编程接口)网关是现代微服务架构中的关键组件,它充当了外部世界与后端服务之间的单一入口点。API网关的主要任务是集中处理所有对外的API请求,提供了包括路由、认证、授权、限...
2. **参数转换**:VB6的数据类型可能与API函数期望的不同,因此需要进行适当的转换。 3. **调用API**:在适当的地方调用已声明的API函数,并传递正确的参数。 4. **处理结果**:根据API函数的返回值和可能的错误代码...
错误处理和文档化也是API设计的关键部分。良好的错误响应应该提供明确的错误代码、描述和可能的解决方案。此外,详细的API文档可以帮助开发者快速理解和使用API,通常包括接口定义、请求和响应示例、调用限制等信息...
1. **API 设计原则**:首先,理解API设计的最佳实践至关重要。这包括遵循RESTful原则,使用清晰的HTTP动词(GET、POST、PUT、DELETE等)来表示操作,以及合理定义URI结构,以便于资源的识别和访问。 2. **错误处理...
### API设计实战——Java框架架构师的实践经验 #### 一、引言 《API设计实战》(Practical API Design: Confessions of a Java Framework Architect)是一本由Java领域经验丰富的专家Jaroslav Tulach撰写的著作。...
数据库API(Application Programming Interface)是软件...这些文件与数据库API设计直接关系不大,但它们是构建和部署Web应用不可或缺的部分。在实际开发中,这些组件将与数据库API结合,共同构成完整的Web应用系统。
显卡的性能与API之间存在着密切的关系,API(Application Program Interface)是软件开发者与硬件设备交互的桥梁。在计算机领域,特别是3D图形处理中,API扮演着至关重要的角色。显卡作为处理图形数据的核心硬件,其...
9. 性能优化:API设计还应关注性能,如减少网络延迟、优化数据传输格式(如使用JSON而非XML)以及实施缓存策略等。 通过上述实践和原则,可以构建出高效、易用且易于维护的微服务API,从而提升整个系统的稳定性和可...
本项目是基于多种语言的FastAPI高性能Web框架设计源码,包含2202个文件,其中包括1120个Python文件、796个Markdown文件、147个PNG文件、52个YAML文件、47个SVG文件、11个Draw.io文件、5个TXT文件和4个Shell文件。...
《API 620-2013:大型焊接低压储罐的设计与建造》是石油和天然气行业的重要标准,由美国石油学会(American Petroleum Institute)发布。这份标准详细规定了在设计、制造、检验和安装大型焊接低压储罐时应遵循的技术...
### IPTV系统的EPG模块API设计与实现 #### 一、引言 随着互联网技术的发展,IPTV(交互式网络电视)作为一种新兴的多媒体服务形式逐渐被广大用户所接受。它利用现有的宽带网络资源为用户提供高质量的视频点播、...
1. **RESTful API设计原则**: REST(Representational State Transfer)是一种网络应用程序的设计风格和开发方式,基于HTTP协议,以简洁和标准化的方式进行数据交换。理解RESTful原则对于创建高效API至关重要,...