如何在 Java 中管理复杂的流程控制?
我在这里研究 Java 流控制和异常处理有一段时间了,并且有这些普遍接受的规则:
- 不要使用异常处理进行流控制,
- 避免检查异常,除非客户端期望恢复(这是一个很好的方式来表达你强制客户端处理异常,以便他们也可以尝试从中恢复?)
以及我尝试遵循的一般规则:
- 如果 doSomething() 无法“做某事”,则应该让调用者意识到这一点
- 方法应该关注做一件事,
这在某些情况下会导致糟糕的组合。我发现我在方法中捕获了已检查的异常,并在各处返回布尔值,并且不断地必须检查连续的调用,做出这样的事情:
if(doA()){
if(doB()){
if(doC()){
// only here do I know it's good
}else{
// I know C failed
}
}else{
// I know B failed
}
}else{
// I know A failed
}
我在某些部分得到了 5-6 个嵌套的 if-else,这非常难看。更不用说管理一堆布尔变量来跟踪哪些有效,哪些无效。
有什么建议吗?这是一个可以接受“goto”的地方吗?
I've been researching Java flow control and exception handling for a while here and have these generally accepted rules:
- don't use exception handling for flow control
- avoid checked exceptions unless the client expects to recover (which is a nice way of saying that you're forcing the client to handle the exception so they might as well try to recover from it?)
Along with general rules I try to follow:
- if doSomething() fails to "do some thing", the caller should be made aware of this
- methods should be concerned with doing one thing
This makes for a bad mix in some cases. I'm finding that I'm catching checked exceptions within methods and returning boolean everywhere and continually having to check successive calls, making something like this:
if(doA()){
if(doB()){
if(doC()){
// only here do I know it's good
}else{
// I know C failed
}
}else{
// I know B failed
}
}else{
// I know A failed
}
I'm getting 5-6 nested if-else's deep in some parts and it's pretty ugly. Not to mention managing a bunch of boolean variables to keep track of what worked and what didn't.
Any advice? Would this be a place where 'goto' is acceptable?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您应该查看
doA()
、doB()
和doC()
。如果它们不太可能失败,则在失败时抛出异常。不合理失败的例子比比皆是,
IOException
、IllegalParameterException
等。如果它们很可能失败,那么
合理失败的例子在Java中不太被强调。一些示例包括当没有更多内容可供读取时,
read()
返回 -1。如果您的doA()
的名称实际上更接近于attemptA()
,那么可能会返回一个boolean
来指示尝试是否成功。想想Collections
接口中的add(...)
和addAll(...)
,它们返回true
如果生成的Collection
被修改。在大多数语言中,传统的 goto 并不是一个好的选择,因为在检查代码时,几乎不可能知道代码“来自”哪里。由于缺乏对进入
goto
之前的状态的了解,因此无法保证进入goto
块之前的一致环境。顺便说一句,这就是为什么传统的goto
在 Java 中不可用,而只有有限的延续goto
可用。要从嵌套不良的结构转换为嵌套较少的结构,请使用一些重构技术:
相当于 相当于
如果
“我知道 A 失败”中的代码包含返回,则您无需担心代码在条件
doA()
为 true 的情况下,落入下面的代码;这样你就可以提升较低的块,如下所示:You should look into the
doA()
,doB()
, anddoC()
. If they are reasonably unlikely to fail, throw an exception when they fail.Examples of unreasonable failures abound,
IOException
,IllegalParameterException
, etc.If they are reasonably likely to fail
Examples of reasonable failures are a bit less emphasized in Java. Some examples include
read()
returning -1 when there's nothing more to read. If yourdoA()
is actually named closer toattemptA()
then perhaps returning aboolean
indicating the success of the attempt is appropriate. Think ofadd(...)
andaddAll(...)
in theCollections
interface, they returntrue
if the resultingCollection
was modified.A traditional
goto
is not a good choice in most languages, as when reviewing the code, it is practically impossible to know where the code "came from." This lack of knowledge of the state prior to entering thegoto
makes it impossible to guarantee a consistent environment before entry to thegoto
block. Incidentally, this is why a traditionalgoto
is not available in Java, and only a limited continuationgoto
is.To covert from a poorly nested structure to one that's less nested, use a few refactoring techniques:
is equivalent to
which is equivalent to
If the code in "I know A failed" includes a return, then you don't need to worry about code under the condition
doA()
being true falling into the code below; so you can promote the lower block, like so:是的。使用异常而不是返回代码。如果您愿意,您可以将异常包装在自己的异常中。
在异常情况下使用异常进行流量控制是完全合理的。通常的建议是避免将它们用于普通流量控制。
Yes. Use exceptions rather than return codes. You can wrap exceptions in your own, if you like.
It is perfectly reasonable to use exceptions for flow control -- in exceptional conditions. The usual recommendation is to avoid their use for ordinary flow control.
我认为你的规则“除非客户端希望恢复,否则不要使用检查异常”是错误的。让调用者根本不恢复,而是将异常传递给调用者,可能会将异常转换为其他内容,这是很常见的。
I think your rule "don't use checked exception unless the client expects to recover" is wrong. It is pretty common to let the caller not recover at all, but instead pass the exception along to its caller, possibly translating the exception to something else.
我会使用状态模式。这样做可以使您的代码更更易于阅读,因为您只能看到从当前状态可以进入哪些可能的状态。
I would use The State Pattern. Doing so can make your code much easier to read as you can only see what possible states you can go to from the current state that you're in.