幸福框架:应当如何使用和处理异常
常见异常使用场景:
- 以写(有副作用)为目的的方法尽量将返回值标记为void,用异常来表示失败,这样你的代码会非常简洁,不会出现大量的条件语句。
- 简洁的版本
1 public void ChangeUsername(Guid userId, string newUsername) 2 { 3 var user = _userRepository.get(userId); 4 5 if(_UsernameRule.Validate(newUsername)) 6 { 7 throw new UsernameInvalidException("用户名不符合规则"); 8 } 9 10 if(_UsernameIndex.Validate(newUsername)) 11 { 12 throw new UsernameIndexException("用户必须唯一"); 13 } 14 15 user.ChangeUsername(newUsername); 16 }
- 更简洁的版本
1 public void ChangeUsername(Guid userId, string newUsername) 2 { 3 var user = _userRepository.get(userId); 4 5 _UsernameRule.DoValidate(newUsername); 6 7 _UsernameIndex.DoValidate(newUsername) 8 9 user.ChangeUsername(newUsername); 10 }
- 简洁的版本
- 以读(无副作用)为目的的方法在出现前者条件不满足时,尽量少用异常而多用“Null Object”,这样你的代码会非常简洁,不会出现大量的条件语句。(省略代码)
常见异常处理策略:
- 让异常冒泡,你做出这种选择,一般是因为在调用层次的下端,此时你不知道如何从异常中恢复,你希望异常冒泡到上层某个集中的地方,在上层做统一的日志操作或其它操作。
- 处理并吞掉异常,你做出这样选择,一般是因为在调用层次的最上端,此时你不希望将异常信息穿过系统边界直接显示在UI中或者导致应用程序终止,你希望在此处对异常做最终的特殊处理,或者将合适的异常信息提示给用户,或者将合适的异常信息记录到日志中。
- 处理并重新抛出异常,你做出这样的选择,一般是因为在调用层次的中间,此时你试着对异常进行一些处理,或者是希望从异常中恢复,或者是希望记录一下异常日志,如果你不能从异常中恢复,最好要重新抛出改异常。
- 处理并抛出新的异常,你做出这样的选择,一般是因为某些异常你希望提示给UI,但是UI中不能显示敏感的信息,因此你选择新建一个异常。
- 处理并抛出包装的异常,你做出这样的选择,一般是为了整合不同的框架,如NHibernate和EntityFramework,你希望Domain层面对的是包装过的异常,这样Domain层的代码才能真正意义的和具体的数据库访问技术解耦。
- 将合理的异常显示给UI,你做出这样的选择,一般是因为你接受了结构化异常处理这一新的理念,而不是采用状态码表示操作结果,此处可以采用AOP机制实现。
- 最终要监控没有被处理的异常,你必须选择这一选项,每种Application类型都提供了自己的选项,请使用。