Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >领域服务上抛异常还是返回错误码

领域服务上抛异常还是返回错误码

作者头像
码农戏码
发布于 2022-06-07 10:40:15
发布于 2022-06-07 10:40:15
80900
代码可运行
举报
文章被收录于专栏:DDDDDD
运行总次数:0
代码可运行

最近收到这样的问题:

领域服务做业务逻辑校验时应该返回错误码还是抛出业务异常?

这其实不算是领域服务的问题,而是Java异常处理[1]问题。

之前总结过一次如何处理异常[2]

上面的文章基本上就解决异常相关问题了。

这儿再回顾总结一下:

返回错误码

在异常没有出现时,像C语言是如何处理问题的?

在 C 语言中,错误码的返回方式有两种:一种是直接占用函数的返回值,函数正常执行的返回值放到出参中;另一种是将错误码定义为全局变量,在函数执行出错时,函数调用者通过这个全局变量来获取错误码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

// 错误码的返回方式一:pathname/flags/mode为入参;fd为出参,存储打开的文件句柄。
int open(const char *pathname, int flags, mode_t mode, int* fd) {
  if (/*文件不存在*/) {
    return EEXIST;
  }

  if (/*没有访问权限*/) {
    return EACCESS;
  }

  if (/*打开文件成功*/) {
    return SUCCESS; // C语言中的宏定义:#define SUCCESS 0
  }
  // ...
}
//使用举例
int fd;
int result = open(“c:\test.txt”, O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO, &fd);
if (result == SUCCESS) {
  // 取出fd使用
} else if (result == EEXIST) {
  //...
} else if (result == EACESS) {
  //...
}

// 错误码的返回方式二:函数返回打开的文件句柄,错误码放到errno中。
int errno; // 线程安全的全局变量
int open(const char *pathname, int flags, mode_t mode){
  if (/*文件不存在*/) {
    errno = EEXIST;
    return -1;
  }

  if (/*没有访问权限*/) {
    errno = EACCESS;
    return -1;
  }

  // ...
}
// 使用举例
int hFile = open(“c:\test.txt”, O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO);
if (-1 == hFile) {
  printf("Failed to open file, error no: %d.\n", errno);
  if (errno == EEXIST ) {
    // ...        
  } else if(errno == EACCESS) {
    // ...    
  }
  // ...
}

错误码没有性能问题,但带来了维护性,尤其没有了异常堆栈信息,失去了快速定位问题的能力。

抛异常

在OO世界中,更推荐使用异常方式,显得更OO些

Checked Exception

Spring创始人Rod Johnson列举了检查异常几个问题:

1、太多的代码

开发人员不得不捕捉他们无法处理的检查异常

2、难以读懂的代码

捕捉不能处理的异常并重新抛出,没有执行一点有用的功能,反而会使查找实际做某件事的代码变得更困难

3、异常无休止封装

4、易毁坏的方法签名

一旦这么多调用者使用一个方法,添加一个额外的检查异常到该接口上将需要这么多代码被修改。也些违背OCP原则[3]

5、检查异常对接口不一定管用

接口有很多种实现,有些实现会出现异常,但有些是不会出现异常的,比如存储数据,放在文件会抛IO相关异常,但数据是数据库,刚不是此异常。

Runtime Exception

运行期异常被很多大牛接受推荐,但也有弊病,就是调用方需要知道内部实现细节,了解抛出了些什么异常,需在javadoc中给出明确说明。

当然我们现在可以使用AOP技术,对异常进行兜底处理,以防泄漏到用户层。

性能

当使用异常时,性能得确会下降,尤其调用链路很深时,更加明显。但异常总归是少数情况,不影响正常情况的性能。

但有些系统对性能要求比较高,怎么办?如正常情况是百万QPS,就算出现少许异常情况,对系统可用性也带来不小的影响。

怎么办?退回错误码时代

但从设计角度可改良一下,可以不再简单返回错误码,如可以使用vavr的Either

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Either<ExceptionMessage,Result> do();

让调用方式来最终确定,当either.isLeft()时,是向上抛异常,还是额外处理。

良好实践

使用检查异常还是运行时异常是个见解问题,不管如何选择,只要团队达成共识,统一规范就可以。

良好的异常,不管是对开发人员,还是运维,用户都应该有全面友好的提示信息

对开发人员,在异常中包含相关信息,使用getMessage()打印日志,方便定位问题

对于用户,可以使用错误代码,字符串比数值语义更明确些。在spring初期代码中,Rod Johnson设计了一个接口ErrorCoded

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface ErrorCoded {

    /** Constant to indicate that this failure isn't coded */
    public static final String UNCODED = "uncoded";

    /** Return the error code associated with this failure. 
     * The GUI can render this anyway it pleases, allowing for Int8ln etc.
     * @return a String error code associated with this failure
     */
    String getErrorCode();

}

其实也不并是说所有场景都去使用异常,如openapi,最好使用errorCode。

异常与契约

乔新亮指出异常是那些让产品无法履行当初承诺用户的契约的问题。

对于异常设计有5点认知:

1、异常一定要消灭;有异常基本就意味着系统存在风险,一定要消灭异常

2、异常一定要管理:消灭异常是个长期工程,短期要通过管理行为来进行控制

3、对异常的处理水平,会极大影响产品的用户体验:用户规模越大,异常的影响往往越大

4、每个异常都要有具体负责人

5、与终端用户相关的异常,要以最高优先级处理

异常设计包含:异常注册、异常事件触发、异常协作流程以及异常统计。

要关注异常数据、异常发生频次、异常数据的增速和降速。

总结

回到起始问题,对于领域服务,自然OO更好些,抛出特定业务异常,业务语义更加清晰。

性能问题,一是避免发生异常情况,二是通过横向扩展。毕竟业务可运维性更重要。

References

[1] Java异常处理: https://www.zhuxingsheng.com/blog/java-exception-practice.html [2] 如何处理异常: https://www.zhuxingsheng.com/blog/java-exception-handling.html [3] OCP原则: https://note.youdao.com/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农戏码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C语言函数大全--m 开头的函数(下)
上述的示例代码,演示了如何使用 mmap() 函数将一个文件映射到内存中,并使用指针 ptr 访问这个映射区域 :
huazie
2025/05/10
1610
C语言函数大全--m 开头的函数(下)
OpenHarmony 实战——LiteOS-A内核中的procfs文件系统分析
procfs是类UNIX操作系统中进程文件系统(process file system)的缩写,主要用于通过内核访问进程信息和系统信息,以及可以修改内核参数改变系统行为。需要注意的是,procfs文件系统是一个虚拟文件系统,不存在硬盘当中,而是系统启动时动态生成的文件系统,储存在内存中。procfs文件系统通常挂载在/proc目录下。
小帅聊鸿蒙
2025/04/24
620
OpenHarmony 实战——LiteOS-A内核中的procfs文件系统分析
[Linux-C/C++] sys/stat.h头文件解析
st_mode 定义了下列数种情况: S_IFMT 0170000 文件类型的位遮罩 S_IFSOCK 0140000 scoket S_IFLNK 0120000 符号连接 S_IFREG 0100000 一般文件 S_IFBLK 0060000 区块装置 S_IFDIR 0040000 目录 S_IFCHR 0020000 字符装置 S_IFIFO 0010000 先进先出(命名管道) 和文件权限标识
QAIU
2023/03/14
2.3K0
stat函数百度百科_strel函数
stat 函数是用来获取文件的各种属性的一个linux下的常用API函数。 函数原型为int stat(const char* path,struct stat* buf); stat定义如下:
全栈程序员站长
2022/09/24
4610
Linux系统编程:基本I/O系统调用
文件描述符 进程每打开一个文件的时候,会获得该文件的文件描述符,而后续的读写操作都把文件描述符作为参数。在用户空间或者内核空间,都是通过文件描述符来唯一地索引一个打开的文件。文件描述符使用int类型表示,文件描述符的范围从0开始,到上限值-1,默认情况下,上限值为1024,也就是说,进程默认情况下最多可以打开1024个文件。负数是不合法的文件描述符,当函数调用出错时,返回的文件描述符为-1。 每个进程都至少包含三个文件描述符: 文件描述符 表示 宏 0 标准输入(stdin) STDIN_FILENO 1
Tencent JCoder
2018/07/02
3.2K0
linux系统编程之文件与I/O(一):文件的打开关闭
本文介绍了Linux系统编程中的文件与I/O操作,包括文件的打开与关闭、文件读写、标准输入输出重定向、文件描述符与文件指针、以及高级文件I/O操作(如异步I/O、缓冲I/O和原子操作)等内容。
s1mba
2018/01/03
1.8K0
Linux文件I/O基础
Linux 文件 I/O(Input/Output)基础是 Linux 应用程序开发中的重要组成部分。在 Linux 系统中,文件 I/O 涉及到文件的读取和写入,以及文件描述符、系统调用等概念。以下是 Linux 文件 I/O 的基础知识:
Linux兵工厂
2024/02/17
1680
Linux文件I/O基础
C语言中open函数「建议收藏」
  int open(const char *pathname, int oflag, … /* mode_t mode */);
全栈程序员站长
2022/09/01
6.3K0
打开文件open()函数的使用方法详解
头文件:#include <sys/types.h>    #include <sys/stat.h>    #include <fcntl.h> 定义函数:     int open(const char * pathname, int flags);     int open(const char * pathname, int flags, mode_t mode); 函数说明: 参数 pathname 指向欲打开的文件路径字符串. 下列是参数flags 所能使用的旗标: O_RDONLY
_gongluck
2018/03/08
2K0
openat与open的区别及用法示例(dfd)
Returns file descriptor on success, or –1 on error 同open相比,多了一个dirfd参数。关于它的用法,参考以下解释:
Laikee
2022/04/25
7940
POSIX之Shared Memory Object
VxWorks支持POSIX的shared memory object - 通过shm_open()获得文件描述符,然后使用mmap()进行映射。shared和private方式都支持。
Taishan3721
2022/06/30
6140
POSIX之Shared Memory Object
C语言-文件编程
C语言标准的文件编程函数: fopen*、fread、fwrite、*fclose
DS小龙哥
2022/01/07
13.5K0
linux超级用户权限 rwx_Linux的RWX权限管理实现详解及chmod使用
前文我们对Linux操作系统的权限管理进行了简要的介绍。今天我们就详细介绍一下关于RWX权限管理的更多细节。很多同学对RWX权限都有一些了解,但是要说出子丑来恐怕就不那么容易了。
全栈程序员站长
2022/09/02
1.8K0
Clean Code系列之异常处理
先前已经对异常如何设计,如何实践异常都写了几篇阐述了。再一次从Clean Code角度来谈谈异常的使用。
码农戏码
2022/11/18
4020
Linux编程下open()函数的用法「建议收藏」
open(打开文件) 相关函数: read,write,fcntl,close,link,stat,umask,unlink,fopen
全栈程序员站长
2022/09/01
2.8K0
Linux内核编程--常见的文件操作
函数执行后,返回的新文件描述符与原有的旧文件描述符共用同一个文件表项,但是文件描述符标志将被清除,进程调用exec时文件描述符将不会被关闭。
Coder-ZZ
2022/05/09
2.1K0
Linux内核编程--常见的文件操作
C++:异常
实际中 C 语言基本都是使用返回 错误码 的方式处理错误,部分情况下使用终止程序处理非常严重的
啊QQQQQ
2024/11/19
890
C++:异常
Linux编程--文件操作
所有执行I/O操作的SystemCall都是以文件描述符(File Describtion,简称fd)来代指打开的文件。它是一个非负小整数。包括:Pipe,FIFO,Socket,终端,设备以及普通的文件。
None_Ling
2019/02/25
1.5K0
Linux编程--文件操作
linux系统调用函数 lstat--获取文件属性
函数功能:用来获取linux操作系统下文件的属性。 函数原型: int stat(const char *pathname,struct stat *buf);
lexingsen
2022/02/24
2.4K0
linux系统调用函数  lstat--获取文件属性
Go - 统一定义 API 错误码
errno.ErrUserPhone、errno.OK 表示自定义的错误码,下面会看到定义的地方。
新亮
2021/01/05
1.3K0
相关推荐
C语言函数大全--m 开头的函数(下)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验