刚刚发表了《什么是契约》一文,突然发现自己通篇都在写理论,没有实例来证明。所以赶快补充一个反面案例——C++ IOStream。说是反面,不是因为IOStream库设计得不精彩(恰恰相反,你很难找到比IOStream设计更为精彩的C++库了),而是想展示一下,在没有契约概念的思想体系里,组件设计将为权责不清的错误处理付出多大的代价。
大家知道,C++ IOStream库非常经典,最先起源于Bjarne Stroustrup的Stream库,之后经过Jerry Schwartz、Martin Carroll、Andy Koenig等人的改进,成为IOStream库,并被并入Bell实验室发行的USD C++库中,广为传播。后来USD库逐渐消亡了,而IOStream由于获得广泛应用,得以幸免,并以新的形式被置于标准库中。
对于错误处理,当IOStream库诞生的时候(大约1985-1987),C++还没有异常机制。因此,Jerry Schwartz发明了这样一套错误处理机制:
例1:经典的IOStream错误处理:
ifstream ifs("filename.txt", ios::in);
if (!ifs) { // 这里实施了向void*转型的操作
// 文件打开失败,实施错误处理
}
先测试文件是否打开,再实施具体操作,这是经典IOStream库的一个惯用法(idiom)。
我们现在设想用户没有很好地执行这个idiom:
int val;
ifstream ifs("filename.txt", ios::in);
ifs >> val;
如果filename.txt打开失败,会发生什么情况?
如果哪位还有当年的Borland C++ 3.1,可以试着测试一下。我估计是什么也不发生,或者说,程序处于极端危险的“undefined behavior”状态。
这种情况对C++库开发者来说是不能接受的。因此,尽管问题的出现是由于用户的错误(他们没有正确地测试流状态),但是由于非契约思想体系下的权责不清,IOStream库的开发者开始追求足以应对用户错误的组件开发技术。由此,IOStream开始在一个方向上被拖入了复杂性的泥潭之中。
我们看看标准库中的对付这种情况采用什么办法。标准IOStream有一个成员函数叫做exceptions(),专门用来帮助程序员切换异常模式。缺省情况下,异常触发并没有打开,所以情形跟经典IOStream库相同。如果你在操作IOStream之前如下调用:
strm.exceptions(std::ios::eofbit | std::ios::failbit |
std::ios::badbit);
则当流不处于good状态时,执行类似 strm >> val;这样的操作时,将会抛出异常。
这样做看起来不错,是吗?
我觉得不是。请恕我C++标准的异议,这是我第一次正式对C++标准中的设计提出异议。
这种设计带来的缺点,首先是复杂。在Nicolai Josuttis的The C++ Standard Library中,对这个机制整整用了5页纸来解释,还意犹未尽。复杂的设计必然带来复杂的使用规则,而面对复杂的使用规则,用户是可以投票的,那就是你做你的,我不用!读这篇帖子的人,谁在实际项目中使用过exceptions()?事实上,我个人是害怕exception甚于害怕undefined behavior。
而对于用户来说,你可以不用,却不得不为对它付出运行性能和空间的代价。诸位有兴趣,不妨追踪一个IOStream功能的实现,看看为了支持这个异常,IOStream库的设计这耗费了多大的心力,而你的CPU又为此耗费了多少clock。
缺点之二,是异常本身的问题——即使你抓到了异常,又当如何?程序可能已经完全离开了发生异常时的执行环境,也许你连异常为什么发生都搞不清楚,谈何处理?也无非就是通知用户一声:“我完了,因为一个异常发生在XXXXXXXX处,你要报告的话给我发Email吧。” 是啊,你还能做什么呢?
我们试着用契约观点来分析这一状况,如果说“先测试,再使用”在传统上是一个idiom,那么在contract思想里上升为一个契约。对于C++来说,应该将“流处于good状态”作为一个契约,在每一个成员函数里进行检查。甚至你还可以设置一个调试期标志,专门用来核查用户是否检查过流状态。在必要的操作进行之前,你可以先用断言检查用户是否检查过流状态,满足了契约。这样一来,在契约之下,用户将被迫以正确的方式使用组件,从而大幅度简化组件开发的复杂度。
再来考虑异常。如果真正发生了异常,在Eiffel中提供了retry机制。Bjarne Stroustrup说过,retry可以做到,但是往往没有意义。为什么没有意义呢?因为C++中没有契约的思想,异常的产生可能根本就是程序员的bug。在这种情况下,无论retry多少次,结果都是一样的糟。可是在Eiffel里情形不同。如果各方面对于契约都做到很好的遵循,那么真正发生异常的时候,我们大可以比较有把握的说,这可能是一个很偶然的事件导致的。比如说网络环境下,另一个用户在那一瞬间突然对文件实施了一个操作,或者硬件的一次偶然异常。对于这种情况,“再试一次”成了合情合理的选择。我们很可能将异常扼杀在摇篮之中,从而不给上层模块带来任何影响。
谁说契约思想不伟大呢?
分享到:
相关推荐
19世纪的经济发展以自由放任主义为理念,传统契约理论便在这样的背景下...这一趋势不同于传统契约理论的是:对垄断协议的规制是基于法定义务的预设而非遵从当事人的约定义务;除关注传统契约构成要件外,更注重"效果要件"。
泛微契约锁 官方操作文档
论文研究-一个新的模糊免赔契约模型.pdf, 论文从保险人期望福利最大化角度研究了不确定环境下的免赔契约设计问题. 将保险人关于投保人私人信息的主观判断刻画为模糊变量...
冒险契约易语言辅助
英文文档 。 代码契约相关。 Code Contracts User Manual Microsoft Corporation March 17, 2013
结果表明:通过设计一个弹性数量契约,获取满意或最优的契约参数,实现信息成本在零售商和供应商之间合理分担,使得双方在信息共享的条件下获取的利益至少不低于在未实现信息共享下所获得的利益,实现双方利益的帕累托...
WCF数据契约实例源码;从最简单的步骤开始,一步一步学会契约的使用
将心理契约划分为发展型责任、规范型责任和人际型责任3个维度,将安全绩效划分为安全结果和安全行为2个维度来设计测量量表,并对407名矿工的心理契约状态和安全绩效水平进行测量与分析,结果表明矿工心理契约与其安全...
EC集成契约锁配置ssl
可以让我们做到对不同的用户授予不同的操作契约的访问,直白一点,比如有两个用户admin、admin2,admin可以访问服务契约中的两个操作契约,但是admin2只能访问其中一个,本示例将实现这种需求,在以下的示例中,服务...
微信小程序 交友互动 小契约(交友互动小程序) (源代码+截图)微信小程序 交友互动 小契约(交友互动小程序) (源代码+截图)微信小程序 交友互动 小契约(交友互动小程序) (源代码+截图)微信小程序 交友互动 ...
契约进入模式包括那些形式.doc
Q萌仙侠手游【契约轮回】Linux手工服务端+运营后台.zip
教育精品资料
针对应急物资的采购和生产能力储备问题, 本文设计了一个基于能力期权契约的双源应急物资采购模型. 在存在一个能力有限的现货市场的前提下, 政府作为唯一的采购方, 期初向...
契约测试pact安装包,解决maven依赖较慢的困恼
论文研究-信息安全外包激励契约设计.pdf, 研究了信息安全外包背景下委托公司如何通过激励措施来协调管理安全服务提供商(MSSP)的努力水平从而有效地控制信息安全风险的...
但是,婚姻契约理论无法解释最近半个世纪以来婚姻关系的发展。夫妻赡养义务、夫妻性自主权等法律关系已经相继突破了传统婚姻契约理论的框架制约,从而促使我们反省婚姻的本质到底是什么。从终极意义上来说,人类的婚姻...
然而突发事件造成零售商面临的需求分布变化时,这个 协调将被打破. 为此给出了供应链对突发事件的最优应对策略,并改进了回购契约使其能协调应对突发 事件,即:改进后的回购契约具有抗突发事件性.
最新房屋买卖契约样本房屋预定买卖契约书范本WORD范文可编辑.docx