可插入错误处理策略

时间:2010-02-10 10:34:07

标签: java design-patterns error-handling

我有服务对象(服务A),它有一些特定的重复异步任务。该服务对象还具有监督对象(服务B)。我想处理与服务A中的特定任务有关的大多数错误,并且仅在需要采取任何其他措施时通知服务B(例如,当服务A不知道如何处理错误时)。

由于服务A依赖于外部资源(例如网络可用性),因此有很多不同的例外情况可以抛出,我现在也不知道所有这些例外情况。

因此,我还希望在服务A中有一个可插入的错误处理策略,以便它可以不同地处理不同的异常。我想用我的IoC容器插入那些策略。

示例A: 服务A应该每30秒下载一次。 (轮询),但URL格式错误,因此抛出MalformedURLException。 A查找MalformedURLExcpetion的错误处理策略,在这种情况下,策略将意味着取消下载并通过回调通知服务B(主管)。

例B: 服务A应该下载一些东西,但主机名无法解析。再次抛出异常(抱歉现在不知道确切的类型)并且将查找相应的策略:在这种情况下,应该停止下载并在另一时间重试,直到达到某个阈值。

我现在的问题:我应该如何实现错误处理策略和策略本身的动态查找?有这种模式吗?

3 个答案:

答案 0 :(得分:2)

嗯,最简单直接的解决方案是使用普通的java try catch,而不是那么灵活,但通常很有用,因为错误处理策略不会经常改变。您无法捕获的那些异常在方法上被声明为可能被抛出,并且可能由您的对象B处理。

如果你想更灵活。为您的服务A创建一个接口,声明所有可能的异常。使用逻辑实现该接口,但没有任何错误处理。然后你可以创建实现接口的ErrorStrategy对象,并将传入的调用委托给该接口的另一个实现,但是这是有趣的部分,为一个或多个特定异常附加了一些错误处理策略。这是一个让它更容易理解的例子。

public interface A {
    void someMethod() throws IOException, MalformedURLException;
}

class AImpl implements A {
    @Override
    public void someMethod() throws IOException, MalformedURLException {
        // here goes your business logic
    }
}

class ErrorHandlerOne implements A {

    @Override
    public void someMethod() throws IOException {
        try {
            delegate.someMethod();
        } catch (MalformedURLException e) {
            // handle the exception
        }
    }
}

如果您想要更灵活,我建议使用AOP机制而不是简单的委托链,这样您就可以轻松插入并交换错误处理策略。如果您使用Spring并且您的Service A是Spring-Bean,您可以轻松地在AOP支持中使用Springs构建。在这种情况下,投掷后的建议就是你要找的。

抛出建议后:如果方法通过抛出异常退出,则执行建议。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

  @AfterThrowing(
     pointcut="com.xyz.myapp.A.someOperation()"
     throwing="ex")
  public void doRecoveryActions(IOException ex) {
    // ...
  }
}

答案 1 :(得分:1)

保持简单并使用普通例外。

它们已经意味着实现不同的错误处理策略:您可以捕获不同的异常并采取相应的行动。只要异常仍然存在,那么您可以实现自己的错误处理方案。否则,如果它真的是控制流程,你应该以不同的方式思考它。

这是伪代码中的一个:

  • CannotDownloadException。当下载真的无法实现时。致命错误。
  • RetryDownlaodException。当下载暂时不可能时。可以实现重试逻辑。

名字不太好,但它说明了原则。

class ServiceB {

  private serviceA serviceA;

  public download( URL url ) throws CannotDownloadException
  {
       try
       {
               serviceA.download( url );
       }
       catch( RetryDownloadException ex )
       {
             // you could have here something more elaborated that would
             // perform a retry strategy based on a configuration (e.g. the number of retry, 
             // or the interval between retry)
             try
             {
                  sleep( 10 sec );
                  serviceA.download( url );
             }
             catch( RetryDownloadException ex )
             {
                  throw new CannotDownloadException();
             }
       }

  }
}

class ServiceA {

  public download( URL url ) throws DownloadException
  {
       try
       {
               if( ! url.isValid() )
                   throws new CannotDownloadException();

               serviceA.download( url );
       }
       catch( ConnectionException ex )
       {
             throw new RetryDownloadException();
       }
   }
}

答案 2 :(得分:0)

尝试观察者模式。通常,您的ServiceA应该使用addFirstTypeExceptionsListener (ServiceListener l) addSecondTypeExceptionsListener (ServiceListener l)

等方法

addExceptionListener (ServiceListener l, ExceptionTypeEnum type)

然后你的ServiceB应该实现ServiceListener接口,它可能有像handleException (Exception e);

这样的方法

如果我们从您的问题中抽象出来,那么使用异常进行流量控制是一种不好的做法。