设计一个好的框架和设计一个好的软件一样,需要考虑的方面很多,比如扩展性、性能、用户体验、稳健性等等,视不同的场景,每个点都可能导致成败,但他们通常并不是老板们关心的,因为在大部分情况下,他们通常都没有做到极限的渴望,或者说相比业务来说,一台机器不够上两台、十台不够上百台,没什么大不了的,反正花的都是客户的钱。当然笔者无论理论上还是实践中都不赞成这些观点,但有些时候也不得不屈服,所以就本文而言,这些都不是重点。

在本文中,笔者想总结一个点,这个点很重要但是又经常被忽略,很多无法产品化或者最后成本高昂的系统有不少都是这一点导致的无以为继,这个点其实《软件框架设计的艺术 》这本书里面反复在说,对于非纯正互联网的软件公司来说,没有深刻的领会这一点是这些公司软件维护成本高昂的根本原因之一,这个点就是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兼容性的设计的更多相关文章

  1. 设计一个JavaScript框架需要编写哪些模块

    在这个js框架随处乱跑的时代,你是否考虑过写一个自己的框架?下面的内容也许会有点帮助. 一个框架应该包含哪些内容? 1. 语言扩展 大部分现有的框架都提供了这部分内容,语言扩展应当是以ECMAScri ...

  2. 高并发架构系列:如何从0到1设计一个类Dubbo的RPC框架

    在过去持续分享的几十期阿里Java面试题中,几乎每次都会问到Dubbo相关问题,比如:“如何从0到1设计一个Dubbo的RPC框架”,这个问题主要考察以下几个方面: 你对RPC框架的底层原理掌握程度. ...

  3. 如何从0到1设计一个类Dubbo的RPC框架

    之前分享了如何从0到1设计一个MQ消息队列,今天谈谈"如何从0到1设计一个Dubbo的RPC框架",重点考验: 你对RPC框架的底层原理掌握程度. 以及考验你的整体RPC框架系统设 ...

  4. 如何设计一个优秀的API(转载)

    最近在整理框架的一些 API,觉得很有必要总结一下 API 兼容性的设计.下图是我自己当下的一些总结,慢慢维护: 网上搜索了一下,一个多月前,“标点符”已经发布了下面这篇文章,觉得写得非常不错,转载于 ...

  5. 好的框架需要好的 API 设计 —— API 设计的六个原则

    说到框架设计,打心底都会觉得很大很宽泛,而 API 设计是框架设计中的重要组成部分.相比于有很多大佬都认可的面向对象的六大原则.23 种常见的设计模式来说,API 设计确实缺少行业公认的原则或者说设计 ...

  6. 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架

    一.系列简述 本篇起,将通过一系列文章,去描述如何构建一个应用开发框架,并以作者开发的框架为例,逐个点展开分析,如何从零开始,构建自己的开发框架. 本系列文章的目的,是带领有一编程经验的人,通过动手, ...

  7. 设计一个分布式RPC框架

    0 前言 提前先祝大家春节快乐!好了,先简单聊聊. 我从事的是大数据开发相关的工作,主要负责的是大数据计算这块的内容.最近Hive集群跑任务总是会出现Thrift连接HS2相关问题,研究了解了下内部原 ...

  8. 自己动手设计并实现一个linux嵌入式UI框架(设计)

    看了"自己动手设计并实现一个linux嵌入式UI框架"显然没有尽兴,因为还没有看到庐山真面目,那我今天继续,先来说说,我用到了哪些知识背景.如:C语言基础知识,尤其是指针.函数指针 ...

  9. 如何在Visual Studio 2017中使用C# 7+语法 构建NetCore应用框架之实战篇(二):BitAdminCore框架定位及架构 构建NetCore应用框架之实战篇系列 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架 NetCore入门篇:(十二)在IIS中部署Net Core程序

    如何在Visual Studio 2017中使用C# 7+语法   前言 之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不 ...

随机推荐

  1. KnockoutJS 3.X API 第四章 数据绑定(3) 控制流if绑定和ifnot绑定

    if绑定目的 if绑定一般是格式是data-bind=if:attribute,if后所跟属性或表达式的值应为bool值(也可以是非bool值,当非空字符串时则为真),if绑定的作用与visible绑 ...

  2. 使用余弦定理制作磁盘形状h5音乐播放器

    目录 [1]功能实现 [2]效果展示 [3]原理说明 旋转原理 余弦定理 [4]代码实现 HTML CSS JS [5]源码查看 功能实现 [1]歌曲播放进度转换成视觉的旋转角度 [2]点击磁盘任意位 ...

  3. CSS命名

    CSS命名规范 CSS样式命名整理 页面结构 容器: container/wrap 整体宽度:wrapper 页头:header 内容:content 页面主体:main 页尾:footer 导航:n ...

  4. 在jfinal中使用druid,并配置查看权限

    首先导入druid包,然后配置configPlugin @Override public void configPlugin(Plugins me) { /**配置druid数据连接池插件**/ Dr ...

  5. 如何使用Jedis操作Redis消息队列

    资源链接 Jedis的jar包 Commons-io的jar包 使用方法 代码样例如下,使用前,注意打开redis的server程序. 代码样例 package RedisExample; impor ...

  6. 【Swift学习】Swift编程之旅(三)

    元组(tuples) tuples是将多个单一的值组合为一个复合的值.它可以包含任何类型的值,而不需要都是相同类型. 一.元组的创建 1. let http404error = (,"NOT ...

  7. 从零开始学习jQuery (二) 万能的选择器

    本系列文章导航 从零开始学习jQuery (二) 万能的选择器 一.摘要 本章讲解jQuery最重要的选择器部分的知识. 有了jQuery的选择器我们几乎可以获取页面上任意的一个或一组对象, 可以明显 ...

  8. MyEclipse中常用的快捷键大全,快来.....

    在这里分享点常用的快捷键,希望对你有帮助! Eclipse的编辑功能非常强大,掌握了Eclipse快捷键功能,能够大大提高开发效率.Eclipse中有如下一些和编辑相关的快捷键. 1. [ALT+/] ...

  9. JS打印页面指定区域

    错误的写法: //打印 function printPage(areaId) { if (parent.$("#PrinFrame").length == 0) { parent. ...

  10. 创建与使用Web API

    今天做了一个练习,实现单独的Web API project.以前只是在ASP.NET MVC project内创建. 创建一个Web API project,可参考下面步骤: 点击OK之后,出现另外一 ...