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

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

    计算监控属性(Computed Observables) 如果你有一个监控属性firstName,和另一个lastName,你要显示的全名?可以使用计算监控属性来实现-它依赖于一个或多个其他监控属性, ...

  2. 浅谈attr()和prop()

    刚开始学JQ的时候 ,看到attr()和prop()这两个属性的时候感觉很迷茫,而且配合官方给出的推荐使用图: prop()可以做到的attr()完全都可以,而且做不到的attr()也可以做到.何用? ...

  3. javase基础复习攻略《六》

    学习JAVA的同学都知道,sun为我们封装了很多常用类,本篇就为大家总结一下我们经常使用的类.上一篇博客一位朋友留言问我String是不是引用数据类型?我通过查找资料发现String属于应用数据类型, ...

  4. 让Team Foundation Server/TFS自动记住用户名密码解决方案

    在使用Team Foundation Server(以下简称TFS) 的时候,在每次打开Visual Studio TFS时候,需要输入用户名和秘密,比较麻烦.现提供一种方法可以解决这个问题: 依次执 ...

  5. [OpenCV] Samples 01: drawing

    基本的几何图形,标注功能. commondLineParser的使用参见:http://blog.csdn.net/u010305560/article/details/8941365 #includ ...

  6. Nutch源码阅读进程5---updatedb

    看nutch的源码仿佛就是一场谍战片,而构成这精彩绝伦的谍战剧情的就是nutch的每一个从inject->generate->fetch->parse->update的环节,首 ...

  7. Foundation 5 发布!最先进的响应式前端框架

    数以百万计的设计师和工程师采用 Foundation 作为他们的产品和网站设计的前端框架.Foundation 是第一个响应式.语义化和移动优先的开源框架.最新发布的 Foundation 5 是最先 ...

  8. 数据库连接池之Proxool使用

    如果想要搭建一个高效的网站,链接池是必须用到的一部分.而连接池的选择是多种多样的.就现在的软件开发界而言,最为多用的是DBCP, c3p0, 和 proxool.而hibernate推荐使用的是c3p ...

  9. Asp.Net工作原理

    1. ASP.NET页面的的一般处理过程: 客户请求WEB页面 WEB服务寻找指令文件(.ASPX) ASP.NET代码被发送给CLR进行编译 HTML流返回给浏览器和指令 浏览器处理HTML并显示页 ...

  10. 开源服务专题之------sshd服务安装管理及配置文件理解和安全调优

    本专题我将讨论一下开源服务,随着开源社区的日趋丰富,开源软件.开源服务,已经成为人类的一种公共资源,发展势头可谓一日千里,所以不可不知.SSHD服务,在我们的linux服务器上经常用到,很重要,涉及到 ...