跨方法通用错误处理的更好设计

3
我有几种方法可以处理相同类型的错误,希望进行重构。我类中的所有方法X都调用binding.X,其中binding是我的依赖类(第三方/ Salesforce soap API,因此我们无法控制抛出的异常类型)。X可以是createupdatedeletequery。对于所有这些方法,我都必须检查异常代码是否为会话过期,然后重新登录并重试该方法。
public void create(SObject[] s){
try{
    binding.create(s);
 } catch (ApiFault e) {
    if(e.getExceptionCode == INVALID_SESSION_ID) {
               //login again
               login();
       //retry
       binding.create(s);
    }
 }
}

更新和删除也遵循相同的模式。查询方法接受不同的参数。

public void query(String query){
    try{
        binding.query(query);
    } catch (ApiFault e) {
        if(e.getExceptionCode == INVALID_SESSION_ID) {
            //login again
               login();
           //retry on specific code
           binding.query(query);
        }
    }
}

因此,我已经在每个方法中放置了对INVALID_SESSION_ID的重试逻辑。是否有更好的设计这些方法的方式?


捕获“异常”是一个非常糟糕的想法。这看起来也像是您正在使用异常来控制流程/业务逻辑,这也是一个糟糕的想法。 - Vivin Paliath
在实际的代码中,它是更具体的异常,但仍然不够具体,以至于我无法在异常上重试。我必须检查异常代码。 - RandomQuestion
如果你想使用Spring的AOP,你可以非常容易地处理这种情况。http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html - Sandeep Nair
“binding” 是第三方的(Salesforce soap API),所以我们无法控制抛出的异常类型。我们必须以这种方式重试,或者我可能想不到更好的方法。 - RandomQuestion
那么我的错误。我以为这是你控制的代码。你确定Salesforce Soap API没有提供另一种方法来检查查询的状态吗?你还可以在问题中提到你正在使用第三方API。 - Vivin Paliath
显示剩余3条评论
1个回答

4
您可以将每个操作封装成一种策略:
private void executeWithLogin(Runnable r) {
    try {
        r.run();
    } 
    catch (Exception e) {
        if (e.getExceptionCode == INVALID_SESSION_ID) {
            //login again
            login();
            //retry on specific code
            r.run();
        }
    }
}

public void create(final SObject[] s){
    executeWithLogin(new Runnable() {
        public void run() {
            binding.create(s);
        }
    });
}

// same for other operations

在Java 8之前,你不得不使用冗长的匿名类来实现这个功能,但是Java 8通过lambda表达式可以使代码更加简洁易读:

public void create(SObject[] s){
    executeWithLogin(() -> binding.create(s));
}

看起来不错。快问一下,如果 binding.create 抛出任何已检查的异常,我需要将它们重新抛出为 RuntimeException,因为我们正在创建 Runnable - RandomQuestion
不需要使用Runnable接口,你可以使用另一个接口,该接口的方法会抛出检查异常并在executeWithLogin()中捕获。例如:interface Action { void execute() throws Exception }。或者直接使用Callable<Void> - JB Nizet
定义自己的接口似乎是合理的。这种方法听起来很完美。 - RandomQuestion
如果签名完全匹配,我不明白为什么会很棘手。Runnable是要运行的操作,并且它完全符合OP想要做的事情(除非绑定操作抛出已检查的异常)。但是,如果您觉得专用接口使事情更易读,请使用它,没有问题。 - JB Nizet
抛出 ApiFault 但不知道是已检查的异常还是未检查的异常。 - nachokk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接