我听说使用控制流异常是不好的做法。你怎么看待这个?
public static findStringMatch(g0, g1) {
int g0Left = -1;
int g0Right = -1;
int g1Left = -1;
int g1Right = -1;
//if a match is found, set the above ints to the proper indices
//...
//if not, the ints remain -1
try {
String gL0 = g0.substring(0, g0Left);
String gL1 = g1.substring(0, g1Left);
String g0match = g0.substring(g0Left, g0Right);
String g1match = g1.substring(g1Left, g1Right);
String gR0 = g0.substring(g0Right);
String gR1 = g1.substring(g1Right);
return new StringMatch(gL0, gR0, g0match, g1match, gL1, gR1);
}
catch (StringIndexOutOfBoundsException e) {
return new StringMatch(); //no match found
}
因此,如果未找到匹配项,则整数将为-1。当我尝试获取子串g0.substring(0, -1)
时,这将导致异常。然后该函数返回一个对象,表明没有找到匹配项。
这是不好的做法吗?我可以手动检查每个索引以查看它们是否全部为-1,但这感觉更像是工作。
更新
我删除了try-catch块并将其替换为:
if (g0Left == -1 || g0Right == -1 || g1Left == -1 || g1Right == -1) {
return new StringMatch();
}
哪个更好:检查每个变量是否为-1,或者使用布尔foundMatch
来跟踪并在最后检查它?
答案 0 :(得分:17)
一般来说,例外是昂贵的操作,正如名称所暗示的那样,特殊条件。因此,在控制应用程序流程的上下文中使用它们确实被认为是不好的做法。
特别是在您提供的示例中,您需要对提供给StringMatch构造函数的输入进行一些基本验证。如果它是一个返回错误代码的方法,以防一些基本参数验证失败,你可以避免事先检查,但事实并非如此。
答案 1 :(得分:10)
我已经对此做了一些测试。在现代JVM上,它实际上不会影响运行时性能(如果有的话)。如果在启用调试的情况下运行,那么它确实会大大减慢速度。
请参阅以下details
(我还应该提到,我仍然认为这是一种不好的做法,即使它不影响性能。最重要的是,它反映了可能很难测试的可能很差的算法设计)
答案 2 :(得分:6)
是的,这是一种不好的做法,特别是当你有办法避免异常时(在尝试索引之前检查字符串长度)。 Try和catch块旨在将“普通”逻辑与“异常”和错误逻辑分开。在您的示例中,您已将“正常”逻辑扩展到异常/错误块中(未找到匹配也不例外)。您也在滥用substring
,因此您可以利用它产生的错误作为控制流。
答案 3 :(得分:6)
程序流程应尽可能直线(因为甚至应用程序变得相当复杂),并使用标准控制流结构。接触代码的下一个开发人员可能不是你,并且(正确地)误解了使用异常而非条件来确定控制流的非标准方式。
在一些遗留代码重构期间,我现在正在针对这个问题略微倾斜。
我发现这个方法的最大问题是使用try / catch打破了正常的程序流程。
在我正在处理的应用程序中(这与您应用的示例不同),异常用于在方法调用内进行通信,即给定结果(例如查找帐号但未查找)发生了。这会在客户端创建意大利面条代码,因为调用方法(在非异常事件期间或正常的用例事件期间)会破坏调用之前执行的任何代码以及catch块。这种方法在一些非常长的方法中重复了很多次,使代码很容易被误读。
对于我的情况,一个方法应该返回每个签名的值,除了真正特殊的事件。异常处理机制旨在发生异常时采用另一个路径(尝试从方法中恢复,以便仍然可以正常返回)。
在我看来,如果你将try / catch块非常紧密地放在一边,你可以这样做;但我认为这是一个坏习惯,可能会导致代码很容易被误解,因为调用代码会将任何抛出的异常解释为'GOTO'类型的消息,从而改变程序流程。我担心虽然这种情况不属于这个陷阱,但这样做往往会导致编码习惯导致我现在生活的噩梦。
那场噩梦并不令人愉快。