契约思想的一个反面案例 (转)

契约思想与C++ IOStream
探讨了在缺乏契约思想背景下C++ IOStream库的错误处理机制及其复杂性,对比了使用契约思想可能带来的简化及优势。
契约思想的一个反面案例 (转)[@more@]

刚刚发表了《什么是契约》一文,突然发现自己通篇都在写理论,没有实例来证明。所以赶快补充一个反面案例——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里情形不同。如果各方面对于契约都做到很好的遵循,那么真正发生异常的时候,我们大可以比较有把握的说,这可能是一个很偶然的事件导致的。比如说网络环境下,另一个用户在那一瞬间突然对文件实施了一个操作,或者硬件的一次偶然异常。对于这种情况,“再试一次”成了合情合理的选择。我们很可能将异常扼杀在摇篮之中,从而不给上层模块带来任何影响。

谁说契约思想不伟大呢?

 


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-993806/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/10752043/viewspace-993806/

改进以下文章: ​ 摘要:         在优快云等平台撰写技术文章时,许多开发者常陷入“知识诅咒”和“自我感动”的误区。本文从选题、结构、代码规范、读者视角和SEO优化五大盲区切入,提供可落地的解决方案,助力技术人产出真正有价值的内容。 一、选题盲区:你以为的热门≠读者的需求 1.1 避开“过度饱和”领域   反例:2024年仍写《Spring Boot快速入门》   正解:             使用Google Trends/优快云热搜挖掘长尾需求             示例选题 :                        《Spring Boot 3.2新特性:虚拟线程如何提升IO密集型性能?》                 《当MyBatis遇见Kotlin DSL:打造类型安全的SQL构建器》 1.2 打造“技术+场景”组合拳     案例:   [Bad] 《Python正则表达式教程》 [Good] 《爬虫工程师必备:用正则表达式突破反爬的3种高阶技巧》 二、内容深度盲区:从“What”到“Why”的跃迁 2.1 拒绝“操作手册式”写作     对比示例: // 浅层写法:   "使用@Autowired实现依赖注入"      // 深度解析:   "Spring IoC容器在启动时如何通过后置处理器完成@Autowired的字段注入?   → 源码追踪:AutowiredAnnotationBeanPostProcessor处理流程   → 坑点:当存在多个候选Bean时,Spring的决策逻辑解析" 2.2 引入“三维分析法”   原理层:算法/框架设计思想   实践层:代码示例+性能对比(附Benchmark)   延伸层:业界应用案例(如Apache某项目中的具体实现) 三、代码展示盲区:90%的技术文章都踩过的坑 3.1 代码规范四要素: 要素:完整性;可读性;版本明确性;异常处理 反面案例:只贴核心片段;无缩进的200行代码块; “最新版本”; 忽略try-catch块 提供可运行的GitHub仓库链接;分步骤折叠代码 + 关键注释;“JDK 21 + Spring Boot 3.2”;展示优雅的异常处理方案 3.2 可视化辅助工具推荐      时序图生成:Mermaid语法示例: sequenceDiagram     Client->>+Service: API请求     Service->>+DB: 查询数据     DB-->>-Service: 返回结果     Service-->>-Client: 响应JSON 四、读者认知盲区:技术传播的“降维打击”艺术 4.1 建立认知阶梯   # 错误:直接抛出复杂概念 def quantum_computing():     pass # 正确:从已知概念迁移 """ 就像经典比特是0/1,量子比特是叠加态 → 用NumPy模拟量子态:q_state = alpha|0> + beta|1> → 代码演示量子纠缠现象 """ 4.2 设计 “渐进式收获感”   每500字设置一个**“Aha Moment”**:   出乎意料的Benchmark结果   巧妙解决某个行业痛点的方案   直观的对比图表(如优化前后QPS对比) 五、传播盲区:酒香也怕巷子深 5.1 优快云平台专属优化技巧      标题公式:          数字量化 + 场景化关键词 + 价值承诺          示例:《3倍性能提升!分布式锁场景下Redisson比原生Redis强在哪?》    发布时间:          工作日早8点(通勤时段) + 晚9点(技术人学习高峰) 5.2 建立内容矩阵   主文章:深度技术解析   配套资源:GitHub代码库 + Docker一键环境   短视频:3分钟结论精讲(适合优快云 App推送) 结语:技术写作的本质是价值交付           避开这五大盲区后,你会发现高质量技术文章的底层逻辑:用专业度建立信任,用可读性降低门槛,用场景化创造价值。立即实践这些方法论,你的下一篇技术博客就有机会获得10倍阅读增长! 行动建议:   复盘最近一篇未爆火的文章,对照五大盲区诊断   在评论区写下你的改进计划,与10万优快云开发者共同成长 ​
04-04
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值