我为我的应用程序不期望的每个条件创建了异常。UserNameNotValidException, PasswordNotCorrectException等。
然而,我被告知我不应该为这些条件创造例外。在我的UML中,那些是主要流程的异常,那么为什么它不应该是异常呢?
是否有创建异常的指导或最佳实践?
我为我的应用程序不期望的每个条件创建了异常。UserNameNotValidException, PasswordNotCorrectException等。
然而,我被告知我不应该为这些条件创造例外。在我的UML中,那些是主要流程的异常,那么为什么它不应该是异常呢?
是否有创建异常的指导或最佳实践?
当前回答
我想说,基本上每一个原教旨主义都会导致地狱。
您当然不希望以异常驱动流结束,但是完全避免异常也是一个坏主意。你必须在两种方法之间找到平衡。我不会为每种异常情况创建异常类型。这是没有成效的。
我通常更喜欢创建两种基本类型的异常,它们在整个系统中使用:LogicalException和TechnicalException。如果需要,可以通过子类型进一步区分这些类型,但通常不是没有必要。
技术异常指的是真正意想不到的异常,比如数据库服务器宕机,到web服务的连接抛出IOException等等。
另一方面,逻辑异常用于将不太严重的错误情况传播到上层(通常是一些验证结果)。
请注意,即使是逻辑异常也不是为了定期使用来控制程序流,而是为了突出显示流何时应该真正结束的情况。在Java中使用时,这两种异常类型都是RuntimeException子类,错误处理是高度面向方面的。
因此,在登录示例中,创建类似AuthenticationException的东西并通过枚举值(如UsernameNotExisting、PasswordMismatch等)来区分具体情况可能是明智的。这样就不会产生巨大的异常层次结构,并且可以将捕获块保持在可维护的级别。您还可以轻松地使用一些通用的异常处理机制,因为您已经对异常进行了分类,并且非常清楚要向用户传播什么以及如何传播。
我们的典型用法是,当用户输入无效时,在Web服务调用期间抛出LogicalException。异常被编组到SOAPFault详细信息,然后在客户机上再次被解组到异常,这将导致在某个web页面输入字段上显示验证错误,因为异常已正确映射到该字段。
这当然不是唯一的情况:您不需要点击web服务来抛出异常。你可以在任何特殊情况下自由地这样做(比如在你需要快速失败的情况下)——这都取决于你的判断。
其他回答
To my mind, the fundamental question should be whether one would expect that the caller would want to continue normal program flow if a condition occurs. If you don't know, either have separate doSomething and trySomething methods, where the former returns an error and the latter does not, or have a routine that accepts a parameter to indicate whether an exception should be thrown if it fails). Consider a class to send commands to a remote system and report responses. Certain commands (e.g. restart) will cause the remote system to send a response but then be non-responsive for a certain length of time. It is thus useful to be able to send a "ping" command and find out whether the remote system responds in a reasonable length of time without having to throw an exception if it doesn't (the caller would probably expect that the first few "ping" attempts would fail, but one would eventually work). On the other hand, if one has a sequence of commands like:
exchange_command("open tempfile"); exchange_command("write tempfile data {whatever}"); exchange_command("write tempfile data {whatever}"); exchange_command("write tempfile data {whatever}"); exchange_command("write tempfile data {whatever}"); exchange_command("close tempfile"); exchange_command("copy tempfile to realfile");
人们会希望任何操作的失败都能中止整个序列。虽然可以检查每个操作以确保操作成功,但如果命令失败,让exchange_command()例程抛出异常会更有帮助。
实际上,在上面的场景中,有一个参数来选择一些失败处理模式可能会有所帮助:从不抛出异常,仅为通信错误抛出异常,或者在命令没有返回“成功”指示的任何情况下抛出异常。
You may use a little bit generic exceptions for that conditions. For e.g. ArgumentException is meant to be used when anything goes wrong with the parameters to a method (with the exception of ArgumentNullException). Generally you would not need exceptions like LessThanZeroException, NotPrimeNumberException etc. Think of the user of your method. The number of the conditions that she will want to handle specifically is equal to the number of the type of the exceptions that your method needs to throw. This way, you can determine how detailed exceptions you will have.
顺便说一下,总是尝试为库的用户提供一些避免异常的方法。TryParse就是一个很好的例子,它的存在使你不必使用int。解析并捕获异常。在您的情况下,您可能希望提供一些方法来检查用户名是否有效或密码是否正确,这样您的用户(或您)就不必进行大量异常处理。这将有望产生更易于阅读的代码和更好的性能。
最终,决定取决于是使用异常处理更有助于处理此类应用程序级错误,还是通过您自己的机制(如返回状态代码)更有帮助。我不认为哪个更好有一个严格的规则,但我会考虑:
Who's calling your code? Is this a public API of some sort or an internal library? What language are you using? If it's Java, for example, then throwing a (checked) exception puts an explicit burden on your caller to handle this error condition in some way, as opposed to a return status which could be ignored. That could be good or bad. How are other error conditions in the same application handled? Callers won't want to deal with a module that handles errors in an idiosyncratic way unlike anything else in the system. How many things can go wrong with the routine in question, and how would they be handled differently? Consider the difference between a series of catch blocks that handle different errors and a switch on an error code. Do you have structured information about the error you need to return? Throwing an exception gives you a better place to put this information than just returning a status.
我想说,对于什么时候使用异常并没有硬性规定。然而,使用或不使用它们有很好的理由:
使用异常的原因:
The code flow for the common case is clearer Can return complex error information as an object (although this can also be achieved using error "out" parameter passed by reference) Languages generally provide some facility for managing tidy cleanup in the event of the exception (try/finally in Java, using in C#, RAII in C++) In the event no exception is thrown, execution can sometimes be faster than checking return codes In Java, checked exceptions must be declared or caught (although this can be a reason against)
不使用异常的原因:
Sometimes it's overkill if the error handling is simple If exceptions are not documented or declared, they may be uncaught by calling code, which may be worse than if the the calling code just ignored a return code (application exit vs silent failure - which is worse may depend on the scenario) In C++, code that uses exceptions must be exception safe (even if you don't throw or catch them, but call a throwing function indirectly) In C++, it is hard to tell when a function might throw, therefore you must be paranoid about exception safety if you use them Throwing and catching exceptions is generally significantly more expensive compared to checking a return flag
一般来说,我更倾向于在Java中使用异常,而不是在c++或c#中,因为我认为异常,无论是否声明,都是函数正式接口的基本组成部分,因为更改异常保证可能会破坏调用代码。在Java IMO中使用它们的最大优势是,您知道调用者必须处理异常,这提高了正确行为的机会。
正因为如此,在任何语言中,我总是从一个公共类派生一层代码或API中的所有异常,这样调用的代码就总能保证捕获所有异常。另外,我认为在编写API或库时抛出特定于实现的异常类是不好的(即从较低的层包装异常,以便调用者接收到的异常在您的接口上下文中是可以理解的)。
注意,Java区分了一般异常和运行时异常,因为后者不需要声明。我只会使用运行时异常类,当您知道错误是由程序中的错误导致的。
异常用于异常行为、错误、失败等事件。功能行为、用户错误等应该由程序逻辑来处理。由于错误的帐户或密码是登录例程中逻辑流的一部分,因此它应该能够毫无例外地处理这些情况。