设计一个较好的框架的难点之一--API兼容性的设计
设计一个好的框架和设计一个好的软件一样,需要考虑的方面很多,比如扩展性、性能、用户体验、稳健性等等,视不同的场景,每个点都可能导致成败,但他们通常并不是老板们关心的,因为在大部分情况下,他们通常都没有做到极限的渴望,或者说相比业务来说,一台机器不够上两台、十台不够上百台,没什么大不了的,反正花的都是客户的钱。当然笔者无论理论上还是实践中都不赞成这些观点,但有些时候也不得不屈服,所以就本文而言,这些都不是重点。
在本文中,笔者想总结一个点,这个点很重要但是又经常被忽略,很多无法产品化或者最后成本高昂的系统有不少都是这一点导致的无以为继,这个点其实《软件框架设计的艺术 》这本书里面反复在说,对于非纯正互联网的软件公司来说,没有深刻的领会这一点是这些公司软件维护成本高昂的根本原因之一,这个点就是API兼容性的设计。如果需求已经大概明确了,那么设计和实现就很重要,但比他们更重要的是作者要想清楚,希望用户如何使用,并仅仅且不多不少提供给用户你想让用户使用的,否则,肯定会有一些用户按照不是你预期但是你开放的接口去使用这个框架,比如说,我们曾在实现一个中间件的时候,处于系统维护目的的考虑,我们要求所有的用户定义接口的时候,需要带一个参数,同时最好继承框架提供的某个基类。于是,某一天,笔者发现,用户进一步增加了限制,他们在业务封装的接口上假设了接口必须有参数,并且没有做防御性编程,最后导致出现了NullPointerException。还有一次,我们在设计接口的时候,希望用户通过自动注入的方式去使用框架,但后面我们发现,用户在有些地方通过java反射的方式直接去获取运行时实例,不过在此场景中,框架一开始设计的时候就处于管理的目的内部提供了相应的API,及时防止了用户通过粗暴的java reflection进行猜测式的硬编码,为后续框架的演变及时消除了后患。
对于很多一次性的项目或者内部系统来说,通常二次开发用户并不多,提供给终端用户的接口通常也是UI层做了严格的封装,所以用户通常不会有太多的机会直接绕过界面访问到系统的脆弱之处。但框架的生命周期通常要比这些系统要长得多,并且在其生命周期中,一个接口或者插件可能会经历数十甚至上百次升级和完善,比如很多广泛使用的开源类库大小版本加起来有些甚至有好几百次。试想,如果这些类库对于常用的接口没有仔细的封装和对假设的防御性保护,导致用户有意或者无意的使用了作者本不希望用户使用的接口,进而导致作者无法从API兼容的角度发布下一版本,这该是多么的崩溃。。虽然从概率的角度来说,这些不希望被违背使用的接口的概率占据所有接口的比例很低、并且他们通常是被高级用户所使用,但这些接口通常比较重要并且会严重危害到软件后续的升级。由于框架涉及的面很广,通常会有很多生产系统运行在其上,并且随着业务的发展,会随之而来在框架上的各种新的需求、甚至和原特性相互冲突的需求,此时,一旦某些接口被按照非预期的方式使用了,要想收回来改变其实现已进行演化就很难了,要想实现随心所欲地按照作者的计划进行发展,首先需要重视的一点事不要让用户接触到不想让他们接触的,而不是通过规章或者要求让他们按部就班的遵守。即使我们可能编写了完善的用户手册、注意事项,绝大部分的用户好像都没有或者不太喜欢去阅读手册和readme,这一点在软件行业,笔者发现开发人员是最不喜欢阅读文档和各种资料的,反而是系统管理员和DBA倒是挺喜欢研究的。
洋洋洒洒扯了半天,现在说说API的分类,通常来说,所有能够被用户直接访问到的程序接口、配置文件、环境变量、插件规范甚至是网卡信息等等只要跟框架相关的都可以算是API。虽然除了程序接口外这些的API并不是看起来那么的显而易见,但是他们的重要性其实不必程序接口低,甚至还有可能比程序接口的副作用更严重,比如假设我们有个控制报文类型的参数,01代表请求,02代表应答,并且当前只有这俩种取值,有些用户可能会if (packetType.equals("01")) ... else ...,这样就给系统的后续衍生留下了后患,因为后面可能会衍生出03代表广播,同时把01的含义修改为P2P请求,虽然01本身含义没有发生变化,但整体的业务范围扩展了,所以对于这些非程序接口的API,同样需要不多不少的对外公开该公开的。其次,对于很多参数来说,如果从上下文能够推导出合理的默认值,就不要强制用户去选择,因为一旦你将该参数设置为可选之后,意味着用户有权不关心该参数、更多的情况则是用户压根不想关心该参数,他们希望他们使用的框架提供了合理的默认值,比如在典型的数据库oracle/mysql/postgresql中,具有大量的参数、优化选项,但在现实中应该来说90%以上的系统都运行于默认参数的状态,这其中很大一部分又是直接使用了安装程序自带的示例配置,如果程序经常不稳定,那就是框架的问题,如果在某个环境有问题,那就是环境的问题,用户从来都不会认为我不了解,所以不稳定,在我接触过的大量开发人员、维护人员中,只有极少数的比例认为是不了解程序、框架特性和适用环境可能导致的系统不稳定。所以,就这一点而言,如果你不希望你开发的框架被弃之不顾,除了提供完成的手册告诉用户他们有权自己决定之外,还必须自己去增加根据上下文计算出合理的你预留用于优化或者维护目的的参数。尤其是在程序语言本身会给相应的变量赋予默认值的情况下,有些异常很可能到了运行是才会触发,对于取值0、1,true、false这些值,甚至很有可能程序运行了数个月之后出现了一个无法解释的bug,而这个bug正是由于作者把选择权交给了用户,而用户并没有选择导致的。
至于这么做的成本和收益,或许大部分的项目经理、部门经理和开发人员都并不在乎或者说没有计算过、甚至可以说不知道如何计算,但是这其实深刻的反映了一个存在已久的定律--墨菲定律。不过我想,那些真正的我们称之为程序员也好、架构师也好、亦或是技术专家也好,他们应该深谙其道、或许他们并没有总结,只是无声无息在实践罢了。
设计一个较好的框架的难点之一--API兼容性的设计的更多相关文章
- 设计一个JavaScript框架需要编写哪些模块
在这个js框架随处乱跑的时代,你是否考虑过写一个自己的框架?下面的内容也许会有点帮助. 一个框架应该包含哪些内容? 1. 语言扩展 大部分现有的框架都提供了这部分内容,语言扩展应当是以ECMAScri ...
- 高并发架构系列:如何从0到1设计一个类Dubbo的RPC框架
在过去持续分享的几十期阿里Java面试题中,几乎每次都会问到Dubbo相关问题,比如:“如何从0到1设计一个Dubbo的RPC框架”,这个问题主要考察以下几个方面: 你对RPC框架的底层原理掌握程度. ...
- 如何从0到1设计一个类Dubbo的RPC框架
之前分享了如何从0到1设计一个MQ消息队列,今天谈谈"如何从0到1设计一个Dubbo的RPC框架",重点考验: 你对RPC框架的底层原理掌握程度. 以及考验你的整体RPC框架系统设 ...
- 如何设计一个优秀的API(转载)
最近在整理框架的一些 API,觉得很有必要总结一下 API 兼容性的设计.下图是我自己当下的一些总结,慢慢维护: 网上搜索了一下,一个多月前,“标点符”已经发布了下面这篇文章,觉得写得非常不错,转载于 ...
- 好的框架需要好的 API 设计 —— API 设计的六个原则
说到框架设计,打心底都会觉得很大很宽泛,而 API 设计是框架设计中的重要组成部分.相比于有很多大佬都认可的面向对象的六大原则.23 种常见的设计模式来说,API 设计确实缺少行业公认的原则或者说设计 ...
- 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架
一.系列简述 本篇起,将通过一系列文章,去描述如何构建一个应用开发框架,并以作者开发的框架为例,逐个点展开分析,如何从零开始,构建自己的开发框架. 本系列文章的目的,是带领有一编程经验的人,通过动手, ...
- 设计一个分布式RPC框架
0 前言 提前先祝大家春节快乐!好了,先简单聊聊. 我从事的是大数据开发相关的工作,主要负责的是大数据计算这块的内容.最近Hive集群跑任务总是会出现Thrift连接HS2相关问题,研究了解了下内部原 ...
- 自己动手设计并实现一个linux嵌入式UI框架(设计)
看了"自己动手设计并实现一个linux嵌入式UI框架"显然没有尽兴,因为还没有看到庐山真面目,那我今天继续,先来说说,我用到了哪些知识背景.如:C语言基础知识,尤其是指针.函数指针 ...
- 如何在Visual Studio 2017中使用C# 7+语法 构建NetCore应用框架之实战篇(二):BitAdminCore框架定位及架构 构建NetCore应用框架之实战篇系列 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架 NetCore入门篇:(十二)在IIS中部署Net Core程序
如何在Visual Studio 2017中使用C# 7+语法 前言 之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不 ...
随机推荐
- 在 xampp 环境下安装 laravel
网上搜了很多教程,很多都是采用命令行安装的.但是对于web前端来说,根本就不知道在干吗! 于是,打算写一篇文章,来教后面的人如何在 XAMPP 环境下安装 laravel. 初步认识 XAMPP(Ap ...
- 实用的开放源码的Excel导入导出类库 CarlosAg ExcelXmlWriter
做企业管理软件经常会遇到要把数据导出成EXCEL格式,目前市面上有很多工具类库可以实现此功能.CarlosAg ExcelXmlWriter是其中之一,它绿色小巧,免安装,又源码开放,我在项目中一直以 ...
- Web Workers 的基本信息
问题:JavaScript 并行性 要将有趣的应用(例如从侧重服务器端的实施)移植到客户端 JavaScript,存在很多制约瓶颈.其中包括浏览器兼容性.静态类型.可访问性和性能.幸运的是,随着浏览器 ...
- Android入门(二)Activity-Toast、Intent
原文链接:http://www.orlion.ga/427/ 一.隐藏activity的标题 在activity的java代码的onCreate()方法中入requestWindowFeature(W ...
- 【博客美化】08.添加"扩大/缩小浏览区域大小" 按钮
博客园美化相关文章目录: [博客美化]01.推荐和反对炫酷样式 [博客美化]02.公告栏显示个性化时间 [博客美化]03.分享按钮 [博客美化]04.自定义地址栏logo [博客美化]05.添加Git ...
- java中线程存活和线程执行的问题!
/* 下面的程序会出现下面的情况,当Thread-0, Thread-1, Thread-2都被wait的时候,可能会同时苏醒 Thread-0 put Thread-1 put Thread-2 p ...
- Java多线程系列--“JUC集合”01之 框架
概要 之前,在"Java 集合系列目录(Category)"中,讲解了Java集合包中的各个类.接下来,将展开对JUC包中的集合进行学习.在学习之前,先温习一下"Java ...
- log4net 中错误 System.Web.HttpException (0x80004005): 文件不存在
用日志组件,Global 中配置的输出最后一个错误信息,总是出现下面的错误信息: 2014-04-01 14:35:41,757 级别:ERROR 信息:[Exception]:System.Web. ...
- java设计模式(六)--观察者模式
转载:设计模式(中文-文字版) 目录: 简单目标任务实现 观察者模式介绍 观察者模式代码实现 观察者模式是JDK中使用最多的模式之一,非常有用.我们也会一并介绍一对多关系,以及松耦合(对,没错,我们说 ...
- Elasticsearch聚合 之 Ip Range IP地址范围聚合
相对于range和date range,这个聚合就是能够表示IP的范围. 普通IP模式 DSL命令: { "aggs":{ "ip_ranges":{ &quo ...