优雅简洁但是错误的代码
蝎子
不能仅仅因为看不到错误处理流程,就认为错误不存在。
有一本C#编程的书中对异常的进行了”高度评价”,下面是该书中的例子代码,我们来看看:
瞧瞧,这代码是多么优雅和简洁,异常确实是个好东西。
嗯,的确是非常简洁,也非常优雅,但是它是错误的。
我们假设在CreateIndexes中出现了异常。GenerateDatabase函数不会捕获这个异常,接下里这个异常会向上层传递。
但是当异常离开GenerateDatabase函数时,重要信息丢失了:数据库创建状态。捕获异常的代码不知道数据库创建的哪一步失败。是否需要删除索引?是否需要删除表?是否需要删除物理数据库?它啥也不知道。
因此,如果调用CreateIndexes时遇到问题,则会永久泄漏物理数据库文件和表。(由于这些文件大概是磁盘上的文件,因此它们会无限期地被系统挂起。)
从某种意义上说,在异常模型中编写正确的代码比在错误代码模型中编写困难,因为任何事情都可能失败,并且你必须为此做好准备。在错误代码模型中,当你必须检查错误时很明显:当你获得错误代码时。在异常模型中,你只需要知道错误可能在任何地方发生。
换句话说,在错误代码模型中,很明显有人无法处理错误:他们没有检查错误代码。但是在抛出异常的模型中,不太容易看出是否有人处理了错误,因为异常不是显式的。
让我们看看下面的例子代码:
在代码中,我们创建了一个Guy对象,然后将他添加到参赛联盟,然后随机地将他加入到一个参赛组中。我们如何简化这段代码呢?
记得:每一行代码都可能发生错误
假设执行”new Guy(name)”时抛出异常?
抛出这个异常时,我们还没有做其他的动作,所以,问题不大。
假设执行”AddToLeague(guy)”时抛出异常?
我们创建的guy对象会被抛弃,而稍后垃圾回收(GC)程序会清理这个对象。
假设执行”guy.Team = ChooseRandomTeam()”时抛出异常?
呃,现在我们有麻烦了。我们已经将该人加入了联盟。如果有人捕获了这个异常,他们将在联盟中找到一个不属于任何球队的人。如果某些代码遍历了联盟的所有成员并使用了guy.Team成员,则由于guy.Team尚未初始化,他们将收到NullReferenceException异常。
在编写代码时,如果每行代码都引发异常会带来什么后果?如果你打算编写正确的代码,则必须执行以下操作。
我们看看下面的代码,通过重新排列调用的顺序来解决问题:
这种看似微不足道的更改对错误恢复有很大影响。通过延迟操作(将人员加入联盟),在人员构造期间采取的任何异常都不会产生任何持久影响。所有发生的事情是,一个部分构造的对象被抛弃了,最终被GC清理了。
一般设计原则:在数据准备就绪之前,不要提交数据
当然,此示例非常简单,因为设置该人员的步骤没有副作用。如果在设置过程中出现问题,我们可以抛弃这个对象,让GC进行清理。
在真实世界中,事情会变得复杂起来。我们看看下面的代码:
以上代码和我们修正过的版本是一样的,唯一的不同时,有人认为,如果每个团队保留一份成员列表,效率会更高,因此你必须将自己添加到要加入的团队中。这对功能的正确性有什么影响?
总结
通过返回错误代码的方式来表达一项操作失败了,是比较简单和直观的,但有些朋友可能会忘记检查每个调用的返回值。
所以,我还是比较喜欢异常这种编码模型,因为你很难忽略它。
最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Cleaner, more elegant, and wrong》
相关文章
- Navicat连接SQL Server2000提示错误08001
- Spring Boot 运行的时候提示日志错误
- java卸载 安装错误_Java卸载后无法重新安装 提示已安装过[通俗易懂]
- 2022-09-02:以下go语言代码输出什么?A:9;B:11;C:编译错误;D:不确定。 package main import ( "fmt"
- 2022-10-31:以下go语言代码输出什么?A:map[];B:nil;C:Panic;D:编译错误。 package main import “fmt“
- 前端代码规范常见错误 一
- 2022-07-26:以下go语言代码输出什么?A:5;B:hello;C:编译错误;D:运行错误。package mainim
- Pycharm 调试代码显示错误行_pycharm远程调试
- 【错误记录】GitHub 提交代码失败、获取代码失败、连接超时、权限错误、ping 请求连接超时 ( 查找域名对应 IP | 设置 host 文件 )
- 【错误记录】集成 Tinker 热修复报错 ( No such property: variantConfiguration for class: .ApplicationVariantData )
- 【错误记录】Android Studio 的 Flutter 代码界面没有 Logcat 面板 ( 2021年08月28日最新解决方案 )
- 【错误记录】无法打开 “xxx“ , 因为 Apple 无法检查其是否包含恶意软件
- 【错误记录】Groovy工程中的文件查找策略 ( main 函数中需要使用 src/main/groovy/Script.groovy | Groovy 脚本直接使用代码相对路径 )
- 解决Linux下PHP运行出错的正确姿势(linuxphp错误)
- macOS Big Sur 11.5 发布 播客功能升级及错误修复
- Akamai的DNS发生故障带来全美大范围互联网连接错误
- Linux终止程序:解决运行错误问题(linux终止程序)
- 1054 MySQL错误解决办法和必要性(1054mysql错误)
- MySQL代码快出现错误无法使用(mysql不能用代码快)
- 无错误提示,MySQL启动失败应该如何处理(mysql不报错无法启动)
- Redis错误127解决之道(redis错误127)
- Oracle下一步操作遭遇错误(oracle下一步就报错)
- JDBC程序的常见错误及调试方法
- js未结束的字符串常量错误解决方法
- Global.asax的Application_Error实现错误记录/错误日志的代码
- 登录远程桌面时遇到“由于客户端检测到一个协议错误(代码0x1104)”