【问题标题】:Best design pattern to skip execution of code if one of the child method throws exception如果子方法之一引发异常,则跳过代码执行的最佳设计模式
【发布时间】:2017-07-26 11:00:27
【问题描述】:

我正在使用核心 java 开发一个测试框架。在我的应用程序中,我有一个调用十个不同子方法的父方法,其中每个方法都可以抛出自己的异常。要求是提供有关父方法执行的详细日志信息,其中包含执行方法所花费的总时间、执行每个子方法所花费的时间、记录任何异常等信息。我为此使用了多个 try catch finally 块。

我的问题是,如果其中一个子方法引发异常,跳过执行剩余子方法的最佳方法是什么?例如:如果方法 4 抛出异常,我想记录该异常并走到最后并记录父方法在此之前所花费的时间。

我尝试在父方法的开头使用布尔变量将其初始化为 true。在执行每个子方法之前,我正在检查布尔变量是否为真。每当抛出异常时,我都会将布尔变量标记为 false。

我的伪代码如下所示:

public String parentMethod(){
    boolean execute = true;
    if(execute) {
        try event1; 
        catch(Event1Exception e1)  
            log e1; execute = false; 
        finally log event 1 time taken;
    }
    if(execute) {
        try event2; 
        catch(Event1Exception e2)  
            log e2; execute = false; 
        finally log event 2 time taken;
    }
    and so on ....
    log total time taken;

在我的实际代码中,我有大约十个不同的事件,它们会抛出自己的异常,有时不止一个。使用这种方法,为每个捕获的异常分配 execute = false 然后再次检查 execute 的值似乎很笨拙。有没有比这更好的方法,不使用任何额外的插件?

【问题讨论】:

  • 如果你有 for(...) { try { execute } catch { handle exception } } 这样的东西,那么你可以很容易地做到这一点:try { for(...) { execute } catch { handle exception } }

标签: java design-patterns


【解决方案1】:

您可以只记录异常并重新抛出它:

public String parentMethod(){
    try {
        try {
            ... 
        } catch(Event1Exception e1) { 
            log e1;
            throw e1;
        } finally {
            log event 1 time taken;
        }

        try {
            ... 
        } catch(Event2Exception e2) { 
            log e2;
            throw e2;
        } finally {
            log event 2 time taken;
        }

    } catch (Event1Exception | Event2Exception | ... e) {
        // Can be safely ignored
    }
}

或者,如果您不想单独处理每个异常,您可以这样做:

public String parentMethod(){
    try {
        try {
            ... 
        } finally {
            log event 1 time taken;
        }

        try {
            ... 
        } finally {
            log event 2 time taken;
        }

    } catch (Event1Exception | Event2Exception | ... e) {
        log e
    }
}

【讨论】:

  • 当一个简单的 try-catch 块可以完美解决这个问题时,这太复杂了
  • 一个 try-catch 会让你不记录每个事件时间
  • 呃,为什么要投反对票?
  • 我在评论中说了为什么我认为它应该被否决,但我实际上重新考虑并试图撤消它。除非您编辑答案,否则我不能这样做。进行一些无关紧要的编辑,我很乐意撤消我的否决
  • 完成。刚刚添加了can be safely ignored
【解决方案2】:

嗨,Sree,我建议在单个 try 块中使用方法调用。在这个 try 块的末尾写下所有具有不同异常的 catch 块。每当任何方法抛出异常时,控制将直接转到相应的catch块,跳过剩余的方法调用。

例如:

try
{
    method1(); // Exception1 occured
    method2(); // skipped
    method3(); // skipped
}
catch(Exception1 e)
{
    // control goes here
    // End timer 

    // Calculate difference
    // Store difference in global variable
}
catch(Exception2 e)
{
}
catch(Exception3 e)
{
}

method1()
{
    // start timer

    // your code

    // End timer, if no exception occurred
    // Store difference in global variable
}

希望这会有所帮助。 :-)

【讨论】:

  • 如果使用这种方法引发异常,我不确定是否能够记录任何子事件所花费的时间。
  • 是的,你也可以计算时间。只需使用全局变量,在方法本身中计算时间并存储在全局变量中。
【解决方案3】:

如果您只对第一个异常感兴趣,那么您可以在单个 try-catch 块中执行所有事件,并在 catch 块中处理任何抛出的异常。

try {
    event1();
    event2();
    // etc
} catch(Exception e) {
    //Handle
}

否则为每个进程启动一个线程并通过在一个失败时终止所有其他线程来处理失败。

【讨论】:

  • 我尝试了这种方法,但如果它引发异常,我无法记录任何子事件所花费的时间。
  • 听起来您需要创建一个专用类来执行具有中断和计时功能的事件,然后使用其中的n。尝试将您的问题分解为易于管理的小块并逐个解决。
【解决方案4】:

这里没有答案:不要那样做。

听起来就像你在这里只是违反了单一责任原则。一个类/方法应该有一个目的,而且只有一个目的!

含义:您必须调用 10 种不同的其他方法已经很可疑 - 在如此复杂的情况下,您认为值得在这里提出一个问题。

因此,我的建议宁愿退后一步,看看您是否可以重构您的代码库,以减少以某种方式捆绑在一起的不同方法调用!

而当你实在无法避免调用 10 个方法时,只需使用 multicatch:

 try {
   foo();
   bar();
   ...
 } catch (ExceptionA | ExceptionB | ...

除此之外:您应该绝对致力于防止代码重复。想到这样的事情:

private interface CallContext<T, X extends Throwable> {
  T execute() throws X;
}

private static <T, X extends Throwable> T runTimed(CallContext<T, X> context) throws X {
     prepare timer
     T result = context.execute();
     put timed value somewhere
     return result;
}

用过这样的东西:

try {
  SomeT t = runTimed(() -> return foo());
} catch (ExceptionA a) ...

【讨论】:

  • 我通常同意遵循 SOLID 原则,但 OP 说他正在制作一个测试框架。所以这个类的目的是“测试其他类的行为”。无论如何,我们可以争论它的复杂性,因为你可以说“创建一个只测试其他类的方法的类”
  • @Pelocho 很好的观察。我想了一会儿,有趣的是,我本周创建了一些使用 lambda 的代码,可以在这里派上用场......相应地更新了我的答案。
  • 我在问题中提到的不同事件是相关事件,例如读取输入参数、建立数据库连接并从数据库中获取相关信息、创建我需要测试的对象实例、实际运行的方法调用对着物体等,
  • 当然......在这种情况下,我的答案的第二部分可能更符合您的需要。但请记住:答案不仅是为 OP 编写的,而且也是为沿途的其他人编写的。他们可能会发现找到其他信息而不只是“直接”答案会很有帮助。
  • 使用 lambdas 相当干净,但如果出现异常,您仍然会错过执行时间
【解决方案5】:

看来你有点想多了。 try-catch 构造已经为您提供了您想要的。考虑一下:

try {
     method1();
     method2();
     method3();
     method4();
 } catch(Exception e) {
   //do error hanlig here (log it for example)
 }  

在这种情况下,假设数学 1 和 2 成功执行,method3 抛出异常 - 控件将进入 catch 块,method4执行。如果我没有正确地理解你,这正是你所需要的

【讨论】:

  • 我尝试了这种方法,但如果它引发异常,我无法记录任何子事件所花费的时间。
  • 甚至在 catch-block 中记录时间?
  • 使用这种方法你会失去时间记录,除非在内部 methodX()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多