设计一个较好的框架的难点之一--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控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不 ...
随机推荐
- js生成随即字符串
js生成随即字符串 /* *js生成随即字符串原来如此简单 *toString() radix argument must be between 2 and 36 */ function uuid() ...
- 关于js中sort()排序方法
第一次写这个,算是记录自己的学习前端的一点点的历程吧.今天在做一个图片的随机排序遇到了一个问题,部分截图如下 我用的是json格式存储数组,想通过排序实现img数组中的内容升序或是降序发现用sort自 ...
- Tcl Tk Introduction
Tcl Tk Introduction eryar@163.com 摘要Abstract:Tcl/Tck脚本可以很容易实现用户自定义的命令,方便的创建图形化的用户界面GUI,所以Tcl和Tk的应用领域 ...
- sql基础语句大杂烩
(坑Open Office,这排版...) 1.distinct列出不同值,过滤掉相同的值 例:company中有两个相同的值比如(apple和apple)时,则只取出一个值 SELECT DISTI ...
- javascript运算符——条件、逗号、赋值、()和void运算符
× 目录 [1]条件 [2]逗号 [3]赋值[4]()[5]void 前面的话 javascript中运算符总共有46个,除了前面已经介绍过的算术运算符.关系运算符.位运算符.逻辑运算符之外,还有很多 ...
- JavaScript的学习--生成二维码
有一些耗cpu的计算,完全可以在客户端上计算,比如生成二维码. qrcode其实是通过计算,然后使用jquery实现图形渲染和画图.支持canvas和table两种方式生成我们所需的二维码. 具体用法 ...
- PHP的学习--Traits新特性
在阅读yii2源码的时候接触到了trait,就学习了一下,写下博客记录一下. 自 PHP 5.4.0 起,PHP 实现了代码复用的一个方法,称为 traits. Traits 是一种为类似 PHP 的 ...
- Apache错误日志时时查看
做项目的时候,有时候会需要查看一下apache的错误日志,然后就需要很繁琐的打开那个目录下面,看信息,只有当错误的时候我才会去打开那个文件. 但是最近我发现在开发的时候,自己忽略掉了很多错误,虽然不会 ...
- 12个免费的 Twitter Bootstrap 后台模板
在互联网上提供很多免费的 Bootstrap 管理后台主题.所有你需要做的就是将它们下载并安装它们,这真的不是什么难事.问题是如何寻找到能够完美符合您的网站需求的主题.当然,你可以自己制作自定义的主题 ...
- [C] 关于表达式求值
结论是:在一个表达式中,如果两个相邻操作符的执行顺序由它们的优先级决定,如果它们的优先级相同,它们的执行顺序由它们的结合性决定.若出现前述规则描述之外的情形,编译器可以自由决定求值的顺序(只要不违反逗 ...