Cleaner, more elegant, and wrong

Just because you can't see the error path doesn't mean it doesn't exist.

Here's a snippet from a book on C# programming, taken from the chapter on how great exceptions are.

try {
AccessDatabase accessDb = new AccessDatabase();
accessDb.GenerateDatabase();
} catch (Exception e) {
// Inspect caught exception
} public void GenerateDatabase()
{
CreatePhysicalDatabase();
CreateTables();
CreateIndexes();
}

Notice how much cleaner and more elegant [this] solution is.

Cleaner, more elegant, and wrong.

Suppose an exception is thrown during CreateIndexes(). The GenerateDatabase() function doesn't catch it, so the error is thrown back out to the caller, where it is caught.

But when the exception left GenerateDatabase(), important information was lost: The state of the database creation. The code that catches the exception doesn't know which step in database creation failed. Does it need to delete the indexes? Does it need to delete the tables? Does it need to delete the physical database? It doesn't know.

So if there is a problem creating CreateIndexes(), you leak a physical database file and a table forever. (Since these are presumably files on disk, they hang around indefinitely.)

Writing correct code in the exception-throwing model is in a sense harder than in an error-code model, since anything can fail, and you have to be ready for it. In an error-code model, it's obvious when you have to check for errors: When you get an error code. In an exception model, you just have to know that errors can occur anywhere.

In other words, in an error-code model, it is obvious when somebody failed to handle an error: They didn't check the error code. But in an exception-throwing model, it is not obvious from looking at the code whether somebody handled the error, since the error is not explicit.

Consider the following:

Guy AddNewGuy(string name)
{
Guy guy = new Guy(name);
AddToLeague(guy);
guy.Team = ChooseRandomTeam();
return guy;
}

This function creates a new Guy, adds him to the league, and assigns him to a team randomly. How can this be simpler?

Remember: Every line is a possible error.

What if an exception is thrown by "new Guy(name)"?

Well, fortunately, we haven't yet started doing anything, so no harm done.

What if an exception is thrown by "AddToLeague(guy)"?

The "guy" we created will be abandoned, but the GC will clean that up.

What if an exception is thrown by "guy.Team = ChooseRandomTeam()"?

Uh-oh, now we're in trouble. We already added the guy to the league. If somebody catches this exception, they're going to find a guy in the league who doesn't belong to any team. If there's some code that walks through all the members of the league and uses the guy.Team member, they're going to take a NullReferenceException since guy.Team isn't initialized yet.

When you're writing code, do you think about what the consequences of an exception would be if it were raised by each line of code? You have to do this if you intend to write correct code.

Okay, so how to fix this? Reorder the operations.

Guy AddNewGuy(string name)
{
Guy guy = new Guy(name);
guy.Team = ChooseRandomTeam();
AddToLeague(guy);
return guy;
}

This seemingly insignificant change has a big effect on error recovery. By delaying the commitment of the data (adding the guy to the league), any exceptions taken during the construction of the guy do not have any lasting effect. All that happens is that a partly-constructed guy gets abandoned and eventually gets cleaned up by GC.

General design principle: Don't commit data until they are ready.

Of course, this example was rather simple since the steps in setting up the guy had no side-effects. If something went wrong during set-up, we could just abandon the guy and let the GC handle the cleanup.

In the real world, things are a lot messier. Consider the following:

Guy AddNewGuy(string name)
{
Guy guy = new Guy(name);
guy.Team = ChooseRandomTeam();
guy.Team.Add(guy);
AddToLeague(guy);
return guy;
}

This does the same thing as our corrected function, except that somebody decided that it would be more efficient if each team kept a list of members, so you have to add yourself to the team you intend to join. What consequences does this have on the function's correctness?

Cleaner, more elegant, and wrong(msdn blog)的更多相关文章

  1. Cleaner, more elegant, and wrong(翻译)

    Cleaner,more elegant,and wrong 整洁,更优雅,但是错的 并不是因为你看不到错误的产生路径就意味着它不存在. 下面是C#编程书中的一个片段,摘自关于异常处理的章节. try ...

  2. 树状数组的笔记√(hzwer blog)

    int lowbit(int x) { return x&(-x); } lowbit()的返回值就是 2^k 次方的值. 求数组的和的算法: (1)首先,令sum=0,转向第二步: (2)接 ...

  3. Alpha 任务状态总览(持续更新)

    Alpha 任务状态总览(持续更新) Part 0 · 简 要 目 录 Part 1 · 流 程 Part 2 · 总 任 务 量 安 排 Part 3 · 爬 虫 任 务 Part 4 · 接 口 ...

  4. Cleaner, more elegant, and harder to recognize (msdn blog)

    It appears that some people interpreted the title of one of my rants from many months ago, "Cle ...

  5. Cleaner, more elegant, and harder to recognize(翻译)

    Cleaner, more elegant, and harder to recognize 更整洁,更优雅,但更难识别 看来,有些人把我几个月前一篇文章的标题"Cleaner,more e ...

  6. js 正则表达式 转至(七郎's Blog)

    //匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线 var re =new RegExp("^[a-zA-Z][a-zA-Z0-9_]{5,19}$"); if( ...

  7. Visual Studio 2010 简体中文旗舰、专业版(MSDN原版下载)

    Visual Studio 2010 简体中文旗舰.专业版(MSDN原版下载)(Visual Studio 2010 ultimate professional x86 dvd)2010[光盘镜像]- ...

  8. Hibernate 配置 转(http://blog.csdn.net/b671900/article/details/39156065)

    做项目必然要先进行数据库表设计,然后根据数据库设计建立实体类(VO),这是理所当然的,但是到公司里做项目后,让我认识到,没有说既进行完数据库设计后还要再“自己”建立一变VO.意思是,在项目设计时,要么 ...

  9. HDU 5813 Elegant Construction(优雅建造)

    HDU 5813 Elegant Construction(优雅建造) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65 ...

随机推荐

  1. cinder块存储控制节点

    #cinder块存储控制节点 openstack pike 安装 目录汇总 http://www.cnblogs.com/elvi/p/7613861.html #cinder块存储控制节点 #在控制 ...

  2. 浅谈canvas绘画王者荣耀--雷达图

    背景: 一日晚上下班的我静静的靠在角落上听着歌,这时"滴!滴!"手机上传来一阵qq消息.原来我人在问王者荣耀的雷达图在页面上如何做出来的,有人回答用canvas绘画.那么问题来了, ...

  3. PhpStorm2017版激活方法、汉化方法以及界面配置

    PhpStorm激活和汉化文件下载网址:http://pan.baidu.com/s/1nuHF1St(提取密码:62cg) PHPMailer的介绍 PhpStorm是一个轻量级且便捷的PHP ID ...

  4. UWP 共享文件——发送者

    这一节,顾名思义,即使你要共享数据给别人,你是数据的提供者.分两步即可1.直接复制代码 protected override void OnNavigatedTo(NavigationEventArg ...

  5. PHP 静态缓存

    今天来说说PHP页面的静态缓存. 根据个人理解,由于客户端重复的请求某个页面,导致该页面短时间内被重复请求相同的数据,导致给服务端一定的压力,同时用户访问速度也会变慢.此时如果把这个页面缓存起来,客户 ...

  6. android应用集成facebook登录

      之前都是做国内的应用开发,没有用过国外的三方登录,比如谷歌登录.facebok登录,最近参与了一个海外的支付相关的项目,调研了一下谷歌登录和facebook登录,其实调研之后觉得也是很简单的,尤其 ...

  7. 《RabbitMQ Tutorial》译文 第 3 章 发布和订阅

    原文来自 RabbitMQ 英文官网的教程(3.Publish and Subscribe),其示例代码采用了 .NET C# 语言. In the previous tutorial we crea ...

  8. Python的控制语句

    1.  控制语句 控制语句是用来改变程序执行的顺序.程序利用控制语句有条件地执行语句,循环地执行语句或者跳转到程序中的其他部分执行语句. Python支持三种不同的控制语句:if,for和while, ...

  9. Cocos2d-X 精灵、动作效果

    命名空间宏: USING_NS_CC; 感觉事实上挺鸡肋的. NS_CC_BEGIN. == using namespace cocos2d{ NS_CC_END ; } 推断一个精灵被点击: 1.层 ...

  10. 游戏服务端中使用Servlet和Java注解的一个好设计

    SNS类游戏基本都是使用HTTP短连接,用Java来开发服务端时能够使用Servlet+Tomcat非常轻松的架构起服务端来.在这里介绍一种使用Servlet比較好的一种设计,我也见过非常多基于HTT ...