简体   繁体   English

返回异常是反模式吗?

[英]Is returning an exception an anti-pattern?

I have two simple methods:我有两个简单的方法:

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)
   }
}

The third method is a private helper method:第三种方法是私有辅助方法:

private Throwable serviceUp() {
    try {
        service.connect();
        return null;
    catch(Exception e) {
       return e;
    }
}

We had a small talk with a colleague of mine about the pattern used here:我们与我的一位同事就此处使用的模式进行了简短的交谈:

returning an Exception (or Throwable ) object from the serviceUp() method .serviceUp()方法返回一个Exception (或Throwable ) object

The first opinion:第一种意见:

It is an anti-pattern to use Exceptions to control the workflow and we should only return boolean from serviceUp() and never the Exception object itself.使用异常来控制工作流是一种反模式,我们应该只从serviceUp()返回 boolean,而不是异常 object 本身。 The argument is that using Exceptions to control the workflow is an anti-pattern.争论是使用异常来控制工作流是一种反模式。

The second opinion:第二种意见:

It is alright, as we need to deal with the object afterwards in the two first methods differently and whether returning Exception object or boolean does not change the workflow at all没关系,因为我们需要在前两种方法中以不同方式处理 object 并且返回异常 object 或 boolean 根本不会改变工作流程

Do you think 1) or 2) is correct and especially, why?您认为 1) 或 2) 是否正确,尤其是为什么? Note, that the question is ONLY about the method serviceUp() and its return type - boolean vs Exception object.请注意,问题仅与方法serviceUp()及其返回类型有关 - booleanException object。

Note: I am not questioning whether to use Throwable or Exception objects.注意:我不是在质疑是否使用 Throwable 或 Exception 对象。

It is an anti-pattern to use exceptions to direct the flow only when the exception is thrown in a non-exceptional situation * . 只有在异常情况下抛出异常时,才使用异常来引导流,这是一种反模式* For example, ending a loop by throwing an exception when you reach the end of a collection is an anti-pattern. 例如,当到达集合的末尾时通过抛出异常来结束循环是一种反模式。

Controlling the flow with actual exceptions, on the other hand, is a good application of exceptions. 另一方面,用实际异常控制流是异常的良好应用。 If your method encounters an exceptional situation that it cannot handle, it should throw an exception, thus re-directing the flow in the caller to the exception handler block. 如果您的方法遇到无法处理的异常情况,则应抛出异常,从而将调用者中的流重定向到异常处理程序块。

Returning a "naked" Exception object from a method, rather than throwing it, is certainly counter-intuitive. 从方法中返回“裸” Exception对象而不是抛出它肯定是违反直觉的。 If you need to communicate the results of an operation to the caller, a better approach is to use a status object that wraps all the relevant information, including the 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);
    }
}

Now the caller would receive CallStatus from serviceUp : 现在调用者将从serviceUp接收CallStatus

CallStatus status = serviceUp();
if (status.isSuccess()) {
    ... // Do something
} else {
    ... // Do something else
    Exception ex = status.getException();
}

Note that the constructor is private, so serviceUp would either return CallStatus.SUCCESS or call CallStatus.error(myException) . 请注意,构造函数是私有的,因此serviceUp将返回CallStatus.SUCCESS或调用CallStatus.error(myException)

* What is exceptional and what is not exceptional depends a great deal on the context. *什么是例外,什么不是例外,在很大程度上取决于背景。 For example, non-numeric data causes an exception in Scanner 's nextInt , because it considers such data invalid. 例如,非数字数据会在ScannernextInt导致异常,因为它认为此类数据无效。 However, the same exact data does not cause an exception in hasNextInt method, because it is perfectly valid. 但是,相同的确切数据不会在hasNextInt方法中导致异常,因为它完全有效。

Second opinion ("it's alright") does not hold. 第二种意见(“没关系”)并不成立。 The code is not alright because returning exceptions instead of throwing them is not really idiomatic. 代码不正常,因为返回异常而不是抛出异常并不是真的。

I also don't buy the first opinion ("using Exceptions to control the workflow is anti-pattern"). 我也不买第一个意见(“使用Exceptions控制工作流程是反模式”)。 service.connect() is throwing an exception and you have to react to this exception - so this is effectively flow control. service.connect()抛出一个异常,你必须对这个异常作出反应 - 所以这有效的流控制。 Returning a boolean or some other state object and processing this instead of handling an exception - and thinking it's not control flow based on an exception is naive. 返回一个boolean或一些其他状态对象并处理它而不是处理异常 - 并认为它不是基于异常的控制流是天真的。 Another disadvantage is that if you decide to rethrow an exception (wrapped in IllegalArgumentException or whatever), you won't have the original exception anymore. 另一个缺点是,如果您决定重新抛出异常(包装在IllegalArgumentException或其他任何内容中),您将不再拥有原始异常。 And this is extremely bad when you try to analyze what actually happened. 当你试图分析实际发生​​的事情时,这是非常糟糕的。

So I'd do the classic: 所以我会做经典:

  • Throw the exception in serviceUp . serviceUp抛出异常。
  • In methods invoking serviceUp : 在调用serviceUp方法中:
    • try-catch , log debug and swallow the exception if you want to proceed on exception. 如果要继续执行异常,请try-catch ,记录调试并吞下异常。
    • try-catch and rethrow the exception wrapped in another exception providing more information on what happened. try-catch并重新抛出包含在另一个异常中的异常,提供有关所发生事件的更多信息。 Alternatively just let the original exception propagate via throws if you can't add anything substantial. 或者,如果您无法添加任何重要内容,只需让原始异常通过throws传播。

It is most important not to lose the original exception. 最重要的是不要失去原来的例外。

Both are wrong 两者都错了

It is alright, as we need to deal with the object afterwards in the two first methods differently and whether returning Exception object or boolean does not change the workflow at all 没关系,因为我们需要在两个第一个方法中以不同方式处理对象,并且返回Exception对象或布尔值是否根本不会改变工作流程

It is not alright. 这不太好。 The concept of exceptions means that they are thrown in, well, exceptional cases. 例外的概念意味着它们被抛入,例外情况。 They are meant to be caught at the place they will be handled (or at the least re-thrown after some local cleanup/logging/etc.). 它们应该被捕获到它们将被处理的地方(或者至少在一些本地清理/记录/等之后重新抛出)。 They are not meant to be handed around like this (that is, in "domain" code). 它们并不意味着像这样(即“域”代码)。

People will be confused. 人们会感到困惑。 Real bugs can easily creep it - for example, what if there is some Exception from some other source than networking here; 真正的错误可能很容易蔓延 - 例如,如果除了网络之外的某些其他来源有一些Exception one you did not anticipate, that is really bad (like some out-of-bounds exception that you created by some programming error)? 一个你没想到的,那真的很糟糕(就像你因某些编程错误而创建的一些越界异常)?

And fresh programmers will be endlessly confused, and/or copy that anti-pattern to places where it just doesn't belong. 新鲜的程序员将无休止地混淆,和/或将反模式复制到它不属于的地方。

For example, a colleague recently implemented a quite complicated interface (as in machine-to-machine interface, not Java interface ), and he did a similar exception handling; 例如,一位同事最近实现了一个非常复杂的界面(如机器到机器界面,而不是Java interface ),他做了类似的异常处理; converting exceptions to silently ignored variations (modulo some log messages). 将异常转换为静默忽略的变体(以某些日志消息为模)。 Needless to say, any exception that he was not actually expecting broke the whole mess in the worst imaginable way; 毋庸置疑,他实际上没有想到的任何例外都以最糟糕的方式打破了整个混乱局面; the opposite of fail fast . 快速失败的反面。

It is an anti-pattern to use Exceptions to control the workflow and we should only return boolean from serviceUp() and never the Exception object itself. 使用Exceptions来控制工作流是一种反模式,我们应该只从serviceUp()返回boolean,而不是从Exception对象本身返回。 The argument is that using Exceptions to control the workflow is an anti-pattern. 争论的焦点是使用异常来控制工作流是一种反模式。

Exceptions most certainly control the workflow in the aspect that they often abort it, or redirect to an error message displayed to the user. 例外情况肯定会控制工作流程,因为它们经常会中止它,或者重定向到显示给用户的错误消息。 It is absolutely possible to have some part of the code "disabled" due to an exception; 由于异常,绝对有可能将某些部分代码“禁用”; ie, exception handling is surely allowed somewhere else than just at the top level of control. 也就是说,除了在最高级别的控制之外,肯定允许异常处理。

But returning exceptions is indeed an anti-pattern; 但返回异常确实是一种反模式; nobody expects that, it is weird, it leads to spurious errors (it is easy to ignore the return value) etc. etc. 没有人期望,这很奇怪,它会导致虚假错误(很容易忽略返回值)等。

So, in the case of your serviceUp() , either make it void - it either works 99% of the time, or throws an exception; 因此,在serviceUp()的情况下,要么使其void - 它要么在99%的时间内工作,要么抛出异常; or make it true boolean in that you fully well accept that it will fail somewhere. 或者使它成为真正的boolean ,因为你完全接受它会在某处失败。 If you do need to hand the error message around, do it as a String , or save it somewhere out of the way, or something, but do not use it as return value, especially not if you intend to throw it again later. 如果您确实需要传递错误消息,将其作为String ,或将其保存在某处,或者某些地方,但不要将其用作返回值,尤其是如果您打算稍后再次throw它。

Easy, standard solution 简单,标准的解决方案

This solution is shorter (less lines, less variables, less if ), simpler, bog-standard and does exactly what you wanted. 这个解决方案更短(线路更少,变量更少,更少, if ),更简单,沼泽标准,并完全符合您的要求。 Easy to maintain, easy to understand. 易于维护,易于理解。

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();
}

I would return a ServiceState which can be, for instance, RUNNING , WAITING , CLOSED . 我会返回一个ServiceState ,例如, RUNNINGWAITINGCLOSED The method would be named getServiceState . 该方法将命名为getServiceState

enum ServiceState { RUNNING, WAITING, CLOSED; }

I have never seen the methods that return an exception as a result of the execution. 我从未见过因执行而返回异常的方法。 For me, when a method returns the value, it means the method finished its work without issues . 对我来说,当一个方法返回值时,这意味着该方法没有问题就完成了它的工作。 I don't want to retrieve the result and parse it on containing any error. 我不想检索结果并解析它包含任何错误。 The result itself means that no failures happened - everything went as planned. 结果本身意味着没有发生任何故障 - 一切都按计划进行。

On the other hand, when the method throws an exception, I need to parse a special object to figure out what went wrong. 另一方面,当方法抛出异常时,我需要解析一个特殊的对象来找出出错的地方。 I don't parse the result, because there is no result . 我不解析结果,因为没有结果

An example: 一个例子:

public void proceedWhenError() {
   final ServiceState state = getServiceState();

   if (state != ServiceState.RUNNING) {
      logger.debug("The service is not running, but it's alright.");
   }
   // do stuff
}

public void doNotProceedWhenError() {
   final ServiceState state = getServiceState();

   if (state != ServiceState.RUNNING) {
      throw new IllegalStateException("The service is not running...");
   }
   // do stuff
}

private ServiceState getServiceState() {
    try {
        service.connect();
        return ServiceState.RUNNING;
    catch(Exception e) {
        // determine the state by parsing the exception
        // and return it
        return getStateFromException(e);
    }
}

If the exceptions thrown by the service is important and/or processed in another place, it along with the state could be saved into a ServiceResponse object: 如果服务抛出的异常很重要和/或在另一个地方处理,那么它与状态一起可以保存到ServiceResponse对象中:

class ServiceResponse {

    private final ServiceState state;
    private final Exception exception;

    public ServiceResponse(ServiceState state, Exception exception) {
        this.state = state;
        this.exception = exception;
    }

    public static ServiceResponse of(ServiceState state) {
        return new ServiceResponse(state, null);
    }

    public static ServiceResponse of(Exception exception) {
        return new ServiceResponse(null, exception);
    }

    public ServiceState getState() {
        return state;
    }

    public Exception getException() {
        return exception;
    }

}

Now, with ServiceResponse , these methods might look like: 现在,使用ServiceResponse ,这些方法可能如下所示:

public void proceedWhenError() {
   final ServiceResponse response = getServiceResponse();

   final ServiceState state = response.getState();
   final Exception exception = response.getException();

   if (state != ServiceState.RUNNING) {
      logger.debug("The service is not running, but it's alright.", exception);
   }
   // do stuff
}

public void doNotProceedWhenError() {
   final ServiceResponse response = getServiceResponse();

   final ServiceState state = response.getState();
   final Exception exception = response.getException();

   if (state != ServiceState.RUNNING) {
      throw new IllegalStateException("The service is not running...", exception);
   }
   // do stuff
}

private ServiceResponse getServiceResponse() {
    try {
        service.connect();
        return ServiceResponse.of(ServiceState.RUNNING);
    catch(Exception e) {
        // or -> return ServiceResponse.of(e);
        return new ServiceResponse(getStateFromException(e), e);
    }
}

returning an Exception is indeed an anti pattern becuase Exceptions should be reserved for errors in the execution, not to describe the condition of the service. 返回异常确实是一种反模式,因为异常应该保留为执行中的错误,而不是描述服务的条件。

imagine if there is a bug in the code of serviceUp() that causes it to throw NullPointerException . 想象一下,如果serviceUp()的代码中有一个错误导致它抛出NullPointerException Now imagine the bug is in the service and the same NullPointerException is thrown from the connect() . 现在假设错误在服务中,并且从connect()抛出相同的NullPointerException

See my point? 看到我的观点?

Another reason is changing requirements. 另一个原因是需求变化。

Currently, the service has two conditions: either up or down. 目前,该服务有两个条件:向上或向下。
Currently. 目前。

Tommorow, you will have three conditions for the service: up, down. Tommorow,你将有三个服务条件:向上,向下。 or functioning with warnings. 或有警告功能。 The day after, you will also want the method to return details about the service in json..... 第二天,您还希望该方法在json中返回有关服务的详细信息.....

The obvious cognitive dissonance is the anti-pattern here. 明显的认知失调是这里的反模式。 A reader will see you using exceptions for flow control and a developer would immediately try to recode it so it does not. 读者会看到您使用流控制的异常,开发人员会立即尝试重新编码它,所以它不会。

My instinct suggests an approach like: 我的直觉暗示了一种方法:

// Use an action name instead of a question here because it IS an action.
private void bringServiceUp() throws Exception {

}

// Encapsulate both a success and a failure into the result.
class Result {
    final boolean success;
    final Exception failure;

    private Result(boolean success, Exception failure) {
        this.success = success;
        this.failure = failure;
    }

    Result(boolean success) {
        this(success, null);
    }

    Result(Exception exception) {
        this(false, exception);
    }

    public boolean wasSuccessful() {
        return success;
    }

    public Exception getFailure() {
        return failure;
    }
}

// No more cognitive dissonance.
private Result tryToBringServiceUp() {
    try {
        bringServiceUp();
    } catch (Exception e) {
        return new Result(e);
    }
    return new Result(true);
}

// Now these two are fine.
public void proceedWhenError() {
    Result result = tryToBringServiceUp();
    if (result.wasSuccessful()) {
        // do stuff
    } else {
        logger.debug("Exception happened, but it's alright.", result.getFailure());
        // do stuff
    }
}

public void doNotProceedWhenError() throws IllegalStateException {
    Result result = tryToBringServiceUp();
    if (result.wasSuccessful()) {
        // do stuff
    } else {
        // do stuff
        throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", result.getFailure());
    }
}

If a method's callers will be expecting that an operation might succeed or fail, and are prepared to handle either case, then the method should indicate, typically via return value, whether the operation failed. 如果方法的调用者期望操作可能成功或失败,并准备处理任何一种情况,则该方法应通常通过返回值指示操作是否失败。 If the callers aren't prepared to usefully handle failure, then the method in which the failure occurred should throw the exception rather than requiring callers add code to do so. 如果调用者没有准备好有效地处理失败,那么发生失败的方法应抛出异常而不是要求调用者添加代码来执行此操作。

The one wrinkle is that some methods will have some callers that are prepared to gracefully handle failures and others that aren't. 一个问题是,有些方法会让一些调用者准备好优雅地处理失败,而另一些则不然。 My preferred approach would be to have such methods accept an optional callback which is invoked in case of failure; 我首选的方法是让这些方法接受一个可选的回调函数,该函数在失败的情况下被调用; if no callback is supplied, the default behavior should be to throw an exception. 如果没有提供回调,则默认行为应该是抛出异常。 Such an approach would save the cost of constructing an exception in cases where a caller is prepared to handle a failure, while minimizing the burden upon callers that aren't. 在调用者准备处理故障的情况下,这种方法可以节省构造异常的成本,同时最大限度地减少呼叫者的负担。 The biggest difficulty with such a design is deciding what parameters such a callback should take, since changing such parameters later is apt to be difficult. 这种设计的最大困难在于决定回调应该采用什么参数,因为稍后改变这些参数往往是困难的。

You can't tell the 2) opinion is false, cause the workflow will run as you want it to run. 你无法告诉2)意见是错误的,因为工作流程将按你希望它运行的方式运行。 From a logical point of view it will run as wanted so it's correct. 从逻辑的角度来看,它会按照需要运行,所以它是正确的。

But it's a really strange and not recommended way to do it. 但这是一个非常奇怪且不推荐的方法。 Firstly because Exception are not designed to do that (which means you are doing an anti-pattern). 首先是因为Exception不是为了这样做而设计的(这意味着你正在做一个反模式)。 It's a specific object designed to be thrown and catch. 它是一个特定的对象,旨在抛出和捕获。 So it's strange to, rather used it as designed, choose tor return it and rather catch it use a if on it. 所以很奇怪,而不是按照设计使用它,选择它返回它,而是抓住它使用if on it。 Moreover you will (maybe it's negligible) face a performance issue cause rather than using a simple boolean as a flag, you instantiate a whole object. 此外,您(可能可以忽略不计)面对性能问题而不是使用简单的布尔值作为标志,您实例化整个对象。

Finally it's also not recommended cause function should return something if the goal of the function is to obtain something (which is not your case). 最后,如果函数的目标是获取某些东西(这不是你的情况),也不建议原因函数返回一些东西。 You should re-designed it to be a function that start a service, then it return nothing cause it won't be called to obtain something, and it will throw an exception if an error happened. 你应该将它重新设计为一个启动服务的函数,然后它不返回任何因为它不会被调用来获取某些东西,并且如果发生错误它将抛出异常。 And if you want to know if your service work create a function designed to give you this info like public boolean isServiceStarted() . 如果你想知道你的服务工作是否创建了一个函数,旨在为你提供像public boolean isServiceStarted()这样的信息。

There are three (idiomatic) to handle functions that can fail during runtime. 有三个(惯用)处理在运行时可能失败的函数。

1. Return boolean 1.返回布尔值

The first one is to return boolean . 第一个是返回boolean This style is widely used in C and C-style APIs, like PHP. 这种风格广泛用于C和C风格的API,如PHP。 This leads - for example in PHP - often to code like this: 这导致 - 例如在PHP中 - 通常代码如下:

if (xyz_parse($data) === FALSE)
    $error = xyz_last_error();

The downsides to this are obvious, this is why it has fallen out of fashion more and more, especially in OOP languages and languages with exception-handling. 这方面的缺点是显而易见的,这就是为什么它越来越不合时宜,特别是在OOP语言和异常处理的语言中。

2. Use an intermediary/state/result object 2.使用中介/状态/结果对象

If you do not use exceptions, you still have the opportunity in OOP languages to return objects that describe the state . 如果不使用异常,则仍有机会使用OOP语言返回描述状态的 对象

For example, if you receive input from the user and want to validate the input, you know beforehand that you might get garbage and that the result will quite often not validate, and you might write code like this: 例如,如果您收到来自用户的输入并想要验证输入,您事先知道可能会出现垃圾,结果通常无法验证,您可能会编写如下代码:

ValidationResult result = parser.validate(data);
if (result.isValid())
    // business logic
else
    error = result.getvalidationError();

3. Use exceptions 3.使用例外

Java does this as you have shown with sockets. 正如您使用套接字所示,Java执行此操作。 The reasoning is that creating a connection should succeed and only fails in exceptional circumstances which need special handling. 原因是创建连接应该成功,并且只有在需要特殊处理的特殊情况下才会失败。

Applying it 应用它

In your case, I would just throw the exception directly, and even drop the helper method.Your helper method is badly named. 在你的情况下,我只是直接抛出异常,甚至删除辅助方法。你的帮助方法命名错误。 It doesn't actually query whether the service is up (as the name suggests), but instead simply connects . 它实际上并不查询服务是否已启动(顾名思义),而是简单地连接

Use (checked) exceptions 使用(选中)例外

Lets assume that connecting is what your method actually does. 让我们假设连接是你的方法实际上做的。 In this case, we name it more aptly connectToService and make it throw the exception directly: 在这种情况下,我们更恰当地命名connectToService并使其直接抛出异常:

public void connectToService() thows IOException {
    // yada yada some init stuff, whatever
    socket.connect();
}

public void proceedWhenError() {
   try {
        connectToService();
   } else {
      logger.debug("Exception happened, but it's alright.", exception)
      // do stuff
   }
}

public void doNotProceedWhenError() throws IllegalStateException {
    try  {
        connectToService();
        // do stuff
    }
    catch(IOException e) {
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

Use boolean 使用boolean

On the other hand, it might be possible that your serviceUp (which is still badly named and would better be called isServiceUp ) does actually query whether the service is running or not (which might have been started by someone else). 另一方面,您的serviceUp仍然命名错误,最好称为isServiceUp )可能会实际查询服务是否正在运行(可能是由其他人启动的)。 In this case, using a boolean would be the proper way 在这种情况下,使用布尔值将是正确的方法

public boolean isServiceUp {
    return ...; // query the service however you need
}

This, of course, refers to the first solution of using booleans and isn't really helpful when you also need to know why the service isn't up. 当然,这是指使用布尔值的第一个解决方案,当您还需要知道服务未解决的原因时,这并不是很有用。

Use intermediary/result/state objects 使用中介/结果/状态对象

So lets dismiss this limited approach and rework it using an intermediary/result/state object: 因此,让我们忽略这种有限的方法,并使用中间/结果/状态对象重做它:

class ServiceState {
    enum State { CONNECTED, WAITING, CLOSED; }

    State state;
    IOException exception;
    String additionalMessage;

    public ServiceState (State state, IOException ex, String msg) {
         [...] // ommitted for brevity
    }

   // getters ommitted for brevity
}

The helper function would become getServiceState : 辅助函数将成为getServiceState

public ServiceState getServiceState() {
    // query the service state however you need & return
}

public void proceedWhenError() {
   ServiceState state = getServiceState();

   if (state.getState() == State.CONNECTED) {
      // do stuff
   } else {
      logger.debug("Exception happened, but it's alright.", state.getException())
      // do stuff
   }
 }

public void doNotProceedWhenError() {
    ServiceState state = getServiceState();

   if (state.getState() == State.CONNECTED) {
      // do stuff
   } else {
      // do stuff
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

Note that you are also repeating yourself in proceedWhenError , the whole code could be simplified to: 请注意,您也在重复执行proceedWhenError ,整个代码可以简化为:

public void proceedWhenError() {
   ServiceState state = getServiceState();

  if (state.getState() != State.CONNECTED) {
      logger.debug("Exception happened, but it's alright.", state.getException())

   }
 // do stuff
}

There is a bit of debate when to use the second case, and when to use the third. 在何时使用第二种情况以及何时使用第三种情况时存在一些争议。 Some people believe that exceptions should be exceptional and that you should not design with the possibility of exceptions in mind, and will pretty much always use the second option. 有些人认为异常应该是例外的,你不应该考虑到例外的可能性,并且几乎总是使用第二种选择。 That is fine. 那样就好。 But we have checked exceptions in Java, so I see no reason not to use them. 但我们已经检查了Java中的异常,所以我认为没有理由不使用它们。 I use checked exceptions when the basic assumption is that the call should succeed (like using a socket), but failure is possible, and I use the second option when its very unclear whether the call should succeed (like validating data). 当基本假设是调用应该成功时(比如使用套接字),我使用了检查异常,但是失败是可能的,当我很不清楚调用是否成功时(比如验证数据),我使用第二个选项。 But there are different opinions on this. 但对此有不同的看法。

It also depends on what your helper method actually does. 它还取决于你的辅助方法实际上做了什么。 isServiceUp implies that it queries some state , but doesn't change it. isServiceUp意味着它查询某个状态 ,但不会更改它。 In that case, returning an state object is obviously the idiomatic way to handle this. 在这种情况下,返回状态对象显然是处理此问题的惯用方法。

But your implementation shows that the helper method connects to the service. 但是您的实现显示辅助方法连接到服务。 In which case, at least in java, the idiomatic way to handle it would be to throw a (checked) exception - but you could also still justify using a result object. 在这种情况下,至少在java中,处理它的惯用方法是抛出(检查)异常 - 但你仍然可以证明使用结果对象。

Simply returning boolean is not advisable, though, since it is too limited. 但是,简单地返回boolean是不可取的,因为它太有限了。 If all you need to know whether the service is running or not (and aren't interested in the cause), such a method might still be useful, though (and could, behind the scenes, be implemented as helper method that just does return getServiceState() == State.SUCCESS ). 如果需要知道服务是否运行与否(和不感兴趣的原因),这种方法可能仍然是有用的,虽然(和可能,在幕后,被实施为只是做辅助方法, return getServiceState() == State.SUCCESS )。


It is an anti-pattern to use Exceptions to control the workflow 使用Exceptions控制工作流是一种反模式

So, lets see, what is an exception? 那么,让我们看看,什么例外?

Definition: An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions. 定义:异常是在程序执行期间发生的事件,它会破坏程序指令的正常流程

Source: https://docs.oracle.com/javase/tutorial/essential/exceptions/definition.html 资料来源: https//docs.oracle.com/javase/tutorial/essential/exceptions/definition.html

The very definition of an exception is that it is an event that disprupts (and thus changes) the workflow of the program. 异常的定义是它是一个异常 (并因此改变)程序工作流的事件。 If using it as defined is an anti-pattern, then the whole language feature in itself must be considered an anti-pattern. 如果按照定义使用它是反模式,则整个语言特征本身必须被视为反模式。 There are people that do believe exceptions are bad, and some have some valid arguments for it, but in general, exceptions are regarded as useful constructs and seen as useful tools to handle exceptional workflows. 有些人确实认为异常是坏的, 有些人一些有效的论据,但一般来说,异常被认为是有用的结构,被视为处理特殊工作流的有用工具。

Throwing an exception is certainly not an anti-pattern. 抛出异常肯定不是反模式。 Returning it , on the other hand, is. 另一方面, 返回它是。 Thus yes, your code - as presented - is non-idiomatic. 因此,您的代码 - 如所呈现的 - 是非惯用的。

This is an anti-pattern: 这是一种反模式:

catch(Exception e) {
   return e;
}

The only reasonable excuse for returning Throwable is to provide detailed failure information to a fast path where failure is expected, without paying the price 1 of exception handling. 返回Throwable的唯一合理理由是向预期失败的快速路径提供详细的故障信息,而无需支付异常处理的价格1 As a bonus, it makes it easy to convert to a thrown exception if the caller doesn't know how to handle this particular situation. 作为奖励,如果调用者不知道如何处理这种特殊情况,它可以很容易地转换为抛出的异常。

But in your scenario, that cost has already been paid. 但在您的方案中,已经支付了该费用。 Catching the exception on behalf of your caller does no one any favors. 代表您的来电者捕获例外不会有任何好处。


1 Pedantically, the direct cost of catching an exception (finding a matching handler, stack unwinding) is pretty low or needed to be done anyway. 1迂腐地,捕获异常的直接成本(找到匹配的处理程序,堆栈展开)非常低或者无论如何都需要完成。 Most of the cost of try / catch compared to a return value is in incidental actions, like building a stack trace. 与返回值相比, try / catch大部分成本都是偶然的操作,比如构建堆栈跟踪。 So whether unthrown exception objects make good storage for efficient error data depends on whether this incidental work is done at time of construction or time of throwing. 因此,未被丢失的异常对象是否能为有效的错误数据提供良好的存储空间取决于这种偶然的工作是在构造时还是在投掷时完成的。 Therefore whether returning an exception object is reasonable may differ between different managed platforms. 因此,返回异常对象是否合理可能在不同的托管平台之间有所不同。

As mentioned in earlier comments "Exception is an event" the exception object that we get is just a detail of the event. 正如前面评论中提到的“异常是一个事件”,我们得到的异常对象只是事件的细节。 Once an Exception is caught in "catch" block and not re-thrown , the event is over. 一旦异常被捕获到“catch”块并且没有被重新抛出,事件就结束了。 Post that you just have a detail object not an exception, although the object is of class exception/throwable. 发布你只是有一个详细对象而不是例外,尽管该对象是类异常/ throwable。

Returning the exception object might be useful because of details held by it but it will add ambiguity/confusion as the calling method is not "handling exception and controlling flow based on exception" . 返回异常对象可能是有用的,因为它所持有的细节但是它会增加歧义/混淆,因为调用方法不是“处理异常并根据异常控制流”。 It is merely using details of a returned object to make decisions. 它只是使用返回对象的细节来做出决定。

So in my opinion a more logical and less confusing way will be to return a boolean/enum based on the exception rather than simply returning exception object. 因此,在我看来,更合乎逻辑且更少混淆的方法是基于异常返回布尔/枚举,而不是简单地返回异常对象。

Not per-se.不是本身。 For example, if you're working with something like AggregateException s from .NET, you have to collect some other exceptions first before you can throw anything.例如,如果您正在使用 .NET 中的AggregateException之类的东西,则必须先收集其他一些异常,然后才能抛出任何东西。 Stuff like that could involve a method that creates (and returns) your exception.类似的事情可能涉及创建(并返回)您的异常的方法。

The more important question in my opinion is, whether or not you intend to throw this exception eventually.在我看来,更重要的问题是,你是否打算最终抛出这个异常。

If not, I think it's bad practice, because this is the one thing the Exception base class does.如果不是,我认为这是不好的做法,因为这是Exception基础 class 所做的一件事。 Other than that it just holds some primitive properties.除此之外,它仅具有一些原始属性。 It would be more semantic (and less confusing for the reader) to create a class with a better name that represents those values.创建一个 class 具有更好的名称来表示这些值,这将更加语义化(并且对读者来说更少混淆)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM