本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-2.html

系列文章:

本文目录

与Exception相关的文件结构

在上一篇我们提到了 集中化管理 Exceptions,这样所有的Exception的产生都在同一文件中,便于我们后期的Review、重构,不会看到漫天飞舞的throw new Exceptions(...),也尽量避免了同一种类的Exception拥有着不同的Message,记录着不同的参数信息。

此外在上一篇中我们还提出了细化Exception的方式,即按照 类型、ErrorCode、Cause 以此细化。

举例来说,项目中会形成如下的文件结构:(实例代码请访问 Github)

绿框里的就是Exception相关的代码文件。其中XXX_ErrorCodes, XXX_ExceptionFactory等类型应该标记为internal,即只是类库内部可见。

在上述文件结构中,我简要的模拟了一个项目的分层结构,不一定都是这样,但大致如下:

  • Common 是项目公共类库,放着各种辅助代码、框架代码等等,代表着你项目的基础部分;
  • Dal 放着连接各种Store(MySql、Redis等等)的代码,即放DataAccessObject的;
  • Repo 屏蔽各种细节,直接为各种Service提供服务;
  • Services 表示你的各项业务模块,当然你的项目结构中有不同的叫法和含义,大家各自知道就行;
  • Application 具体的应用,提供Endpoint,开放Service的各项功能,面向调用者。

为什么要详细描述一个项目分层结构,因为一会儿我们需要解决一个问题,就是Exception的可见性。

先卖个关子,一会儿再说,在此之前,先解决一个小问题。

ExceptionFactory 和 Ensure

将Exception集中化管理,除了我们用的ExceptionFactory,还有一种写法我们经常见到,那就是Ensure,或者EnsureXXX。

参阅.net core的源代码,我们会看到非常多的Ensure类,源代码连接 https://source.dot.net/#q=Ensure。

我们经常把 复杂 或者 重复 出现的判断写到Ensure中,Ensure的中文意思“确保”正是这个含义。

示例代码如下:

// Ensure 写法
internal static class Ensure
{
public static void NickNameNotExisted(string newNickName)
{
bool alreadyExisted = true; // Some Check if(alreadyExisted)
{
throw IdentityExceptionFactory.NickNameExisted();
}
} public static T NotNull<T>([NotNull]T? obj, string paramName) where T : class
{
if (obj == null)
{
throw new ArgumentNullException(paramName);
}
return obj;
}
}

我们观察到,Ensure类中的Exception也是由ExceptionFactory生成的(当然那些基础Exception类型,比如ArgumentNullException就直接new 了)。

Ensure类是对判断(if语句等)的抽象,而ExceptionFactory只是Exception的生成器。

当然,当我们不直接在现场抛出异常,而是调用Ensure类方法抛出异常,会稍微有一些不同:

  • Stack Trace不同,第一行永远是Ensure的方法

    • 没有什么大不了,因为Stack Trace会详细记录后续的调用;
  • 代码逻辑上不如直接写 throw 那么直白
    • 明确使用Ensure这一称呼,写到代码规则里,团队内统一,一看到Ensure就知道可能要抛出异常。
  • 注意CodeAnalysis 和 NRT(null reference types)
    • 如果项目开启上面两项功能,需要添加相应的Attribute。比如,上面代码的NotNull方法,否则在Ensure后,CodeAnalysis还会提醒你CS8602等错误.

Exception的可见性

举例来说,就是 DalException 要不要对Service可见,还是只对Repo可见?

其实可见或者不可见都没有明显的错误,但总的来说,

  • 隐藏并包装下层的Exception,对调用者更友好些。

    • 比如在WebApplication项目中,我们就可以只面对来自Service的Exception,不用去知道Dal或者Repo的细节。
    • 而来自IdentityService的异常,只有IdentityException一种(不算ArgumentNull那些基本Exception)
  • 隐藏并包装下层的Exception,可以在每一层加入更多有用的信息
  • 如果是调用第三方类库,尽量隐藏和包装成自己的异常类型。比如你在Dal中调用MySqlConnector类库,那么将MySqlException放到DalException的innerException中去。让程序的其他部分不需要面对一个可能更换掉的外来者。

结语

以上写的都比较具体,所以如果与大家的实际使用不同,请多多交流,共同进步,我也会补充到文章里来。

下一篇,我会讲讲关于 捕捉Exception的几点实际经验,比如全局异常处理以及异步编程中的异常。

谢谢阅读。

最新文章

  1. ZOJ Problem Set - 1331 Perfect Cubes 判断一个double是否为整数
  2. MyBatis学习总结(三)&mdash;&mdash;优化MyBatis配置文件中的配置
  3. Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage&#160;规范标准化草案解决方案ThreadStatic
  4. 条件查询,有input和select框,当查询条件获取焦点时支持摁下enter键查询
  5. Js和asp.net各自设置的cookie相互读取的方法
  6. Oracle11g TNS-12541:TNS无监听程序
  7. jQuery 停止动画
  8. Sum Root to Leaf Numbers 解答
  9. tigervnc*
  10. JAVA知识的相关积累--用于自己以后查找
  11. 【Android开发日记】第一个任务Android Service!Service靴+重力感应器+弹出窗口+保持执行
  12. linux下如何开放80端口
  13. 用Beautifulsoup 来爬取贴吧图片
  14. C++ 指针和引用 吐血整理 Pointer&amp;Reference
  15. 基于vue 、vue-router 、firebase的todolist小项目
  16. Exp4 恶意代码分析 ——20164325王晓蕊
  17. linux shell 指令 诸如-d, -f, -e之类的判断表达式简介
  18. Linux(centos7)如何安装Zend Optimizer Zend Guard Loader
  19. 将button或者input角变为圆弧
  20. 电子印章在Odoo的实现步骤

热门文章

  1. taro 禁用滚动事件
  2. 【目标检测】用Fast R-CNN训练自己的数据集超详细全过程
  3. Vue和Element基础使用,综合案例学生列表实现
  4. 【Notes】现代图形学入门_02
  5. 微信小程序:添加全局的正在加载中图标效果
  6. JS输出为[object object]
  7. Spring-03 依赖注入(DI)
  8. Go语言实现布谷鸟过滤器
  9. Python函数注解
  10. 解决tui-editor布局错误失效问题(Vue-Element-Admin)