Mar*_*nha 19 java error-handling exception-handling anti-patterns exception
我有两个简单的方法:
public void proceedWhenError() {
Throwable exception = serviceUp();
if (exception == null) {
// do stuff
} else {
logger.debug("Exception happened, but it's alright.", exception)
// do stuff
}
}
public void doNotProceedWhenError() {
Throwable exception = serviceUp();
if (exception == null) {
// do stuff
} else {
// do stuff
throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
}
}
Run Code Online (Sandbox Code Playgroud)
第三种方法是私有帮助方法:
private Throwable serviceUp() {
try {
service.connect();
return null;
catch(Exception e) {
return e;
}
}
Run Code Online (Sandbox Code Playgroud)
我和我的同事就这里使用的模式进行了一次小谈:
从方法返回Exception
(或Throwable
)对象serviceUp()
.
第一个意见:
使用Exceptions来控制工作流是一种反模式,我们应该只从
serviceUp()
Exception对象本身返回boolean,而不返回 它.争论的焦点是使用异常来控制工作流是一种反模式.
第二个意见:
没关系,因为我们需要在两个第一个方法中以不同方式处理对象,并且返回Exception对象或布尔值是否根本不会改变工作流程
你认为1)或2)是否正确,尤其是,为什么?
请注意,这个问题是只有大约方法serviceUp()
和它的返回类型- boolean
VS Exception
对象.
注意:我不会质疑是否使用Throwable或Exception对象.
das*_*ght 30
只有在异常情况下抛出异常时,才使用异常来引导流,这是一种反模式*.例如,当到达集合的末尾时通过抛出异常来结束循环是一种反模式.
另一方面,用实际异常控制流是异常的良好应用.如果您的方法遇到无法处理的异常情况,则应抛出异常,从而将调用者中的流重定向到异常处理程序块.
Exception
从方法中返回"裸" 对象而不是抛出它当然是违反直觉的.如果需要将操作的结果传递给调用者,更好的方法是使用包装所有相关信息的状态对象,包括异常:
public class CallStatus {
private final Exception serviceException;
private final boolean isSuccess;
public static final CallStatus SUCCESS = new CallStatus(null, true);
private CallStatus(Exception e, boolean s) {
serviceException = e;
isSuccess = s;
}
public boolean isSuccess() { return isSuccess; }
public Exception getServiceException() { return serviceException; }
public static CallStatus error(Exception e) {
return new CallStatus(e, false);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,呼叫者将收到CallStatus
来自serviceUp
:
CallStatus status = serviceUp();
if (status.isSuccess()) {
... // Do something
} else {
... // Do something else
Exception ex = status.getException();
}
Run Code Online (Sandbox Code Playgroud)
请注意,构造函数是私有的,因此serviceUp
将返回CallStatus.SUCCESS
或调用CallStatus.error(myException)
.
*什么是例外,什么不是例外,在很大程度上取决于背景.例如,非数字数据会导致Scanner
s中的异常nextInt
,因为它认为此类数据无效.但是,相同的精确数据不会导致hasNextInt
方法中的异常,因为它完全有效.
lex*_*ore 17
第二种意见("没关系")并不成立.代码不正常,因为返回异常而不是抛出异常并不是真的.
我也不买第一个意见("使用Exceptions控制工作流程是反模式").service.connect()
抛出一个异常,你必须对这个异常作出反应 - 所以这是有效的流量控制.返回一个boolean
或一些其他状态对象并处理它而不是处理异常 - 并且认为它不是基于异常的控制流是天真的.另一个缺点是,如果您决定重新抛出异常(包装IllegalArgumentException
或其他),您将不再拥有原始异常.当你试图分析实际发生的事情时,这是非常糟糕的.
所以我会做经典:
serviceUp
.serviceUp
:
try-catch
,如果要继续执行异常,请记录调试并吞下异常.try-catch
并重新抛出包含在另一个异常中的异常,提供有关所发生事件的更多信息.或者,throws
如果您不能添加任何实质内容,只需让原始异常传播.最重要的是不要失去原来的例外.
没关系,因为我们需要在两个第一个方法中以不同方式处理对象,并且返回Exception对象或布尔值是否根本不会改变工作流程
这不太好.例外的概念意味着它们被抛入,例外情况.它们应该被捕获到它们将被处理的地方(或者至少在一些本地清理/记录/等之后重新抛出).它们并不意味着像这样(即"域"代码).
人们会感到困惑.真正的错误可能很容易蔓延 - 例如,如果有一些Exception
来自其他来源而不是网络,那该怎么办?一个你没想到的,那真的很糟糕(就像你因某些编程错误而创建的一些越界异常)?
新鲜的程序员将无休止地混淆,和/或将反模式复制到它不属于的地方.
例如,一位同事最近实现了一个非常复杂的界面(如机器到机器界面,而不是Java interface
),他做了类似的异常处理; 将异常转换为静默忽略的变体(以某些日志消息为模).毋庸置疑,他实际上没有想到的任何例外都以最糟糕的方式打破了整个混乱局面; 快速失败的反面.
使用Exceptions来控制工作流是一种反模式,我们应该只从serviceUp()返回boolean,而不是从Exception对象本身返回.争论的焦点是使用异常来控制工作流是一种反模式.
例外情况肯定会控制工作流程,因为它们经常会中止它,或者重定向到显示给用户的错误消息.由于异常,绝对有可能将某些部分代码"禁用"; 也就是说,除了在最高级别的控制之外,肯定允许异常处理.
但返回异常确实是一种反模式; 没有人期望,这很奇怪,它会导致虚假错误(很容易忽略返回值)等.
所以,在你的情况下serviceUp()
,要么成功void
- 它要么在99%的时间内工作,要么抛出异常; 或者说它是真的boolean
,因为你完全接受它会在某个地方失败.如果你确实需要传递错误信息,可以将其作为a String
,或者将其保存在某个地方,或者其他地方,但不要将其用作返回值,尤其是如果您throw
以后再打算再使用它.
这种解决方案更短(线路更少,变量更少,更少if
),更简单,沼泽标准,并且完全符合您的要求.易于维护,易于理解.
public void proceedWhenError() {
try {
serviceUp();
// do stuff (only when no exception)
}
catch (Exception exception) {
logger.debug("Exception happened, but it's alright.", exception)
// do stuff (only when exception)
}
}
public void doNotProceedWhenError() {
try {
serviceUp();
// do stuff (only when no exception)
}
catch (Exception exception) {
// do stuff (only when exception)
throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
}
}
private void serviceUp() {
service.connect();
}
Run Code Online (Sandbox Code Playgroud)
返回异常确实是一种反模式,因为异常应该保留用于执行中的错误,而不是描述服务的条件。
想象一下代码中是否存在serviceUp()
导致其抛出的错误NullPointerException
。现在想象一下错误是在服务中,并且同样NullPointerException
是从connect()
.
明白我的观点了吗?
另一个原因是需求的变化。
目前,该服务有两个条件:启动或关闭。
现在。
明天,您将遇到服务的三个条件:向上、向下。或运行时带有警告。第二天,您还需要该方法以 json 格式返回有关服务的详细信息......
归档时间: |
|
查看次数: |
4136 次 |
最近记录: |