好吧,我承认我是标题党,还是让我们从一个故事开始吧。
项目的业务逻辑层需要被设计成一个具备易扩展的模式,对外提供了大小相异的API。项目组人人头脑风暴,最后在各位的努力下,克服苦难,业务逻辑层被封装起来,一组最初的API被提供出来:
1、现有Service逻辑已经疏于管理,欠缺重构,变成了不易控制的逻辑层,接口众多,鱼龙混杂,难以规整出清晰、可用的接口给第三方(例如下游定制团队),怎么办?
Web应用有个特点,当你对代码的管理缺乏控制而搞不定时,可以在其上封装一层,这是一个通用的解决办法,也是一柄双刃剑。
正如某同事“我们都是工程商人”所述,心底里可能我们愿意追求代码的曼妙和清晰,但是大多数软件项目都应建立在“赚钱”的前提之上。
于是大家各抒己见,最终将原有Service之上的Facade改头换面,重新整顿了一把,变成了XXXManagementUtil。
2、有人考虑扩展方便,将这些API门面类中的方法参数,全部使用Event封装起来,这样的好处在于添加参数的情况下不必修改任何接口方法:
class UserManagementUtil{
public UserInfo getUser(GetUserEvt);
}
3、Event里面的参数哪些可选、哪些必选?应当满足怎样的取值规则?于是在Event的接口中引入了verify方法,子类中重写了toString方法,并利用Spring的AOP机制,在API调用时进行参数校验并打印日志。
4、API的接口应该根据什么来划分呢?
按照模型驱动来划分吧,有声音说,比如UserManagementUtil、SongManagementUtil。
可是更多的声音说:业务中有Song、Music等等二十多种内容类型,不觉得太庞大了吗?还是把Song、Music等发布的内容涉及到的API都归结到ContentManagementUtil和ContentExtManagementUtil里面吧,不要细分了。
5、API的接口粒度应该怎样控制呢?
有同事表示,提供简易的接口,就如同Windows提供的API一样,那么,我们提供基于模型的CRUD方法吧,这样方法既原子、纯净,通过外部调用者一定的组合,又能满足外部调用的功能。
6、怎样让API便于外部调用呢?
一开始要求外部使用Spring注入的方式来使用API的建议遭到了一些反对,我们不是要让调用方用得灵活方便、降低定制难度对吗?
又有一个声音说:把方法都变成static吧?于是又遭来一些反对的声音,static方法可能带来API中资源依赖和资源初始化的问题。
最后API外部的调用变成了下面这样,而API内依然由Spring来管理:
UserManagementUtil.getInstance().getUser(evt);
7、怎样让API便于定制人员理解呢?
需要强化API的JavaDoc,其中需要包含足够的方法功能和流程、参数以及返回值的说明。
……
于是大家大干一场,API渐渐新鲜出炉了,一切看起来是那么美好。
--------------------------------------------------------------------------------
不过数日之后,许多人渐渐开始发现,看起来那么美好的事情实际上好像也并不那么美好:
1、考虑到外部接口调用功能和性能的问题,通用和简单的接口已经完全无法满足业务需要,多次API调用可能意味着多次与数据资源交互,如果能把多次交互合并成一次(例如底层使用一次数据库连表查询实现),性能就可以大大提升。于是一些功能庞大的接口开始出现,并且愈来愈不受控制了。
2、方法参数Event的境遇如何呢?也不好。调用者并不十分清楚Event中哪些参数是必选的,那么,就把所有参数都传进去吧!于是API以外的Action层面到处是这样丑陋的代码:
GetUserEvt event = new GetUserEvt();
event.setAccountName("13000000000");
event.setAddress("xxx");
event.setType(xx);
event.setAge(xx);
……(此处省略N行)
3、verify方法呢?toString方法呢?
随着项目的进行,这些都变得不可控了,写一个简单根据ID来获取模型POJO的方法,就要写一个Evt,还有一堆冗长的verify和toString方法,开发人员变得不那么情愿,这两个方法就写得越来越简陋了。
更糟的是,这里用到的Spring的AOP方式还在项目中被发现为性能瓶颈,于是有更多的人开始怀疑最初这个决定的正确性了。
4、ContentManagementUtil和ContentExtManagementUtil变得非常庞大,困惑越来越多,一些方法也不知该往哪放了,比如getContentsByCategory方法,到底是放到ContentManagementUtil里呢,还是放到CategoryManagementUtil里呢?
5、JavaDoc变成了真正定制的瓶颈,定制人员不断表示无法读懂JavaDoc,不知道该怎样调用API;而开发人员呢,则不断抱怨JavaDoc工作量巨大,要把一个接口的JavaDoc写清楚,需要描述接口内部流程、参数名称、参数含义、使用场景、不同场景下需要哪些参数、返回对象含义、异常类型、异常返回码可能的取值、调用示例……一句话,变得无比困难。
6、需求变化频繁,当接口版本更新时,接口调用者发现,糟糕,原来的方法调用变得不可用了,但是是哪个参数不正确或缺失造成的呢?也没法看出来。
……
--------------------------------------------------------------------------------
这就是一个简简单单的API风云录,一段API诞生和撞到新秀墙的困惑史,一切看起来都很自然,也许你感到些许熟悉,我就说到这里,如果有一些感触,这些真实的记录就变得有价值。
我说完了。
……
可是我怎么甘心就这么“说完了”?
这不是我的风格。
我要痛批一顿?要引出所谓“真正正确”的做法?
当然不是!这样的事情还是留给专家学者去做吧。
--------------------------------------------------------------------------------
API的设计是软件架构设计的细化和缩影,是一件持续的工作,一样没有银弹,一样没有一劳永逸的可能。它历来是一个难题,无论在最初看似多么“完美”的规划和安排,最后都可能变成鸡肋;而且,看起来越强大、兼容性越好的设计,就越有可能打了水漂。
1、既然给不出一个完整和绝对正确的办法,API从诞生之日起,就需要开发人员不断对其修整和维护,使之不断适应当前应用需要,从而避免其老化。软件的架构需要维护,一个再出色的架构师完成了他的设计,如果开发团队不能贯彻并把基本的架构思想传递下去,项目一样还会偏离预想的轨道(不一定是好或者坏的结果,但通常都是不合预期的),这一点,API也一样,设计人员应当参与API一版版的发展和发布。
对于一些业务性很强的API,需要API编写的门槛会提高,需要开发人员理解API的原则,清楚细化了的要求。
2、设计一个易用、简单和清晰的API基本规则,在API发展的过程中,大小规模的重构不可避免且理所应当,基本规则就像软件架构一样,不会轻易变更,最初设定的规则越复杂,后续变更和成熟的过程越痛苦。重构本身就是版本发展的一部分,更多的特性应当在后续的重构中丰满,而不是在最初预留好一个准许“上帝功能”都能便捷扩展的能力。
3、保持基础接口的兼容性。JDK的HashTable有一个containsValue方法,还有一个contains方法,二者功能上完全一样,之所以搞这样两个完全一样的方法,正是由于历史原因造成的。JDK1.2才正式引入Java Collections Framework,抽象了Map接口,才有了containsValue方法,而之前的方法因为向下兼容的原因无法删除。我不能评估说这是一个好的兼容还是一个糟糕的妥协,但至少,JDK都为了保持了基础接口的兼容性而做了这样一件看似不合理的事。
有一种不激进的思路是,给一些将要废弃的接口置为@deprecated,待若干个版本后可以选择删除。
4、给API设计人员以充分的信任。API的设计不是民主选举,少数服从多数,把不可抗拒的要求和额外的需求陈述清楚,就不应过于干涉其组织的讨论。通常软件的设计都有这样一个惯性问题,不是最终采纳合理的方案、成熟的方案,而是采纳具备话语权的人的意见,或是经过民主式的妥协来完成设计。
5、严格控制接口数量。性能、可维护性,二者谁更重要?事实上这两者在很多情况下都是一组矛盾,平衡二者的关系才是设计者应当考虑的。如果把数个行为放置到一个接口中,当然可以提升性能,但是也增加了接口,增大了维护成本。尤其对于成熟的API来说,每增加一个接口都应是慎重的行为,如果项目组自我管理能力不够,就需要专人集中守护。
6、发布稳定和成熟的API。业界有一句玩笑话叫“不要使用3.0版本以下的软件”,正是说的这个道理,经过少数几轮迭代的API还远不稳定,而且可能还有众多bug,后续大规模的变更就会令API失去价值。如果由于不可抗因素,API变更实在太大,考虑提纯API的功能,尽量简单的方法,将复杂的关系条件交给调用方,会减小需求变更带来的冲击。
举例来说,UserInfo模型最初设计的相关接口有:
queryUserInfoByName(String name)
queryUserInfoByAccountNumber(String accountNumber)
但倘若模型变更频繁,那么可以考虑设计这样的接口:
queryUserInfo(Map queryCriteria)
其中的参数QueryCriteria代表着了查询条件,比如这样调用:
queryUserInfo({name:"%abc%",accountNumber:"139%"})
(降低了可读性和调用的便捷程度,但提高了接口稳定性)
7、接口尽量独立,避免发布互相之间有依赖关系的接口。如果实在避免不了,最好让两个有依赖关系的接口放置在相近的地方,以便查看。
8、接口必须被完全理解,最好简洁易懂。如果接口复杂,那你可能寄望于详尽的JavaDoc来说明,如果接口简单,完全可以只需要很少的说明,成为自注释的。
最后我用一张Google API的截图结束本文:
文章系本人原创,转载请注明作者和出处
- 大小: 110.6 KB
分享到:
相关推荐
《Spring4企业开发实战风云录》是一本深入探讨Spring 4.0框架在企业级应用中的实战技巧与技术原理的著作。Spring作为Java领域最流行的轻量级框架,它的4.0版本带来了许多改进和新特性,使得它在现代企业开发中占据了...
11. **API接口**:风云房产站4.0可能开放API,允许与其他系统(如物业管理系统、房源信息同步平台)进行数据交换,实现更广泛的合作与联动。 12. **维护与升级**:考虑到软件的持续发展,系统应设计有良好的可维护...
同时,使用JavaScript进行业务逻辑处理,通过微信小程序框架提供的API与微信服务器进行数据交互,实现功能如用户登录、数据存储、网络请求等。 对于“水浒传”主题的应用,开发者可以设计各种与故事相关的功能模块...
《三国风云》是一款深受玩家喜爱的在线游戏,其自动化测试是保证游戏稳定性和用户体验的关键环节。这个名为"Sanguo.rar"的压缩包包含了与《三国风云》自动化测试相关的Python代码,旨在帮助开发者或测试人员更好地...
7. **兼容性广泛**:风云压力测试白金版适用于多种应用环境,包括Web应用、数据库、API接口等,兼容HTTP/HTTPS、FTP、SMTP等多种协议。 8. **易用性**:提供直观的用户界面,使得非专业人员也能快速上手进行性能...
9. **API接口**:为了与其他系统集成,可能会提供API接口,允许开发者创建插件或实现特定功能,如自动化消息处理。 10. **日志与错误处理**:系统的后台应包含完善的日志记录和错误处理机制,便于排查问题和性能...
【数据库连接与操作】为了与MySQL数据库交互,项目中可能使用了JDBC(Java Database Connectivity),这是Java平台的标准API,用于连接和访问数据库。通过JDBC,Servlet可以建立到数据库的连接,执行SQL查询,插入、...
- **访问 SDK 参考**:SDK 文档包含详细的 API 参考,可以帮助开发者了解可用的 API 函数及其用法。 #### 九、XenServer API 概述 - **API 概述**:介绍了 XenServer API 的基本概念,如身份验证、会话管理、对象...
3. **风云天气小程序**:这是一个提供本地天气信息并允许用户记录个人日常的小程序。宁先生在此负责地址搜索、天气数据请求、主页面展示以及云数据库操作。使用的开发技术包括微信小程序、云开发、Vant UI、F2-...
本文将详细介绍如何通过MFC提供的API和控件来实现这一功能。根据给定的代码片段,我们可以看到一个MFC应用程序如何在多文档界面(MDI)环境下切换到全屏模式以及如何退出全屏模式。 #### 全屏模式开启函数:...
它基于Apache许可下的BPMN 2.0标准,旨在提供一个简单易用的API,以帮助开发人员构建工作流应用,并通过流程驱动应用开发来支持业务流程的自动化。 在Activiti 5.22.0版本中,数据库结构设计得非常清晰,主要分为...
我们可以创建一个专门的类来封装雷赛控制卡的操作,如`雷赛运动控制器.cs`,在这个类中定义各种操作方法,如`StartMotor()`、`StopMotor()`、`SetPosition()`等,这些方法会调用SDK中的API函数完成实际的控制任务。...
在定时关机HTA中,VBScript用于处理用户交互,读取和设置关机时间,并调用Windows API函数来实现定时关机的功能。例如,可以使用`WScript.Shell`对象的`Run`方法配合特定命令(如`shutdown /s /t`)来安排关机任务。...
6. **权限请求**:别忘了在运行时请求`ACCESSIBILITY_SERVICE`权限,因为从Android 6.0(API级别23)开始,一些敏感权限需要在运行时动态请求。 7. **最后的测试**:确保你的设备开启了对应服务的访问权限,然后...
3. **RESTful API**:为了获取天气数据,应用可能通过调用外部天气API(如OpenWeatherMap或Weather Underground)获取数据。这通常涉及HTTP请求和JSON解析,可能使用了诸如HttpURLConnection、OkHttp或Spring的...
VB作为Microsoft开发的面向对象的编程环境,它提供了丰富的控件和API,使得开发者可以方便地构建与数据库交互的用户界面。Access则是微软公司推出的一种关系型数据库管理系统,以其易于使用和强大的功能在中小型企业...
这些组件提供了方便的API,可以轻松处理文件上传操作,包括读取上传的文件、保存到服务器等。 4. **文件保存**:服务器接收到文件后,需要将其保存到服务器的一个特定目录。通常会为每个上传的文件创建一个唯一的...
5. 合法域名设置:尽管描述中提到“无需设置”,但在实际发布小程序时,需要确保所使用的网络服务(如API接口调用)的域名已添加到微信小程序的合法域名列表中,否则可能导致网络请求失败。 其次,车险计算涉及到的...
// 外部API调用声明 // // // //***********************************************// [DllImport("User32.DLL")] public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam,int lParam); ...