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

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

    git忽略以点开头的文件夹 好像不是什么问题,可是我用的时候不好使,还是记录下 参考:http://www.oschina.net/question/1437985_2181276

  2. Liferay7 BPM门户开发之45: 集成Activiti文件上传部署流程BPMN模型

    开发文件上传,部署流程模板. 首先,开发jsp页面,deploy.jsp <%@ include file="/init.jsp" %> <h3>${RET ...

  3. 锋利的jQuery——19个jQuery 常用片段整理

    /** * Created by yu on 2016/11/20 0020. */// 1.禁用页面右键菜单$(function () { $(document).on('contextmenu', ...

  4. WP中的语音识别(下):语音指令

    除了系统集成的可以用于搜索.启动应用程序等语音命令外,在我们的应用程序内部还能自己定义语音指令,使得我们的APP能与语音操控结合得更加完全. 语音指令是通过一个XML文件来定义的.比如,咱小舅子开了家 ...

  5. mac下网页中文字体优化

    最近某人吐槽某门户网站在mac下chrome字体超丑,然后发现虽然现在mac用户越来越多,但是大家依然无视mac下的字体差异,于是研究了下mac下网页中的中文字体,和大家分享. 看了一遍国内各大门户和 ...

  6. KlayGE 4.4中渲染的改进(二):DR的其他改进

    转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2749   上一篇讲了TBDR的实现,本篇继续讲解deferred rendering层的一些 ...

  7. 深入理解javascript函数系列第一篇——函数概述

    × 目录 [1]定义 [2]返回值 [3]调用 前面的话 函数对任何一门语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即 ...

  8. [OpenCV] Samples 05: convexhull

    得到了复杂轮廓往往不适合特征的检测,这里再介绍一个点集凸包络的提取函数convexHull,输入参数就可以是contours组中的一个轮廓,返回外凸包络的点集 ---- 如此就能去掉凹进去的边. 对于 ...

  9. NODE.JS学习的常见误区及四大名著

    NODE.JS学习的常见误区及四大名著 前段时间由于不满于社区里很多人对于NODE.JS的种种误解而写了一篇文章名为: NODE.JS之我见:http://www.cnblogs.com/pugang ...

  10. git代码回滚:Reset、Checkout、Revert的选择

    代码回滚:Reset.Checkout.Revert的选择 Zhongyi Tong edited this page on Dec 8, 2015 · 5 revisions Pages 19 Ho ...