How to best implement a set of methods, even if an exception occurs

In the current Java project, we have code similar to the following example:

try { doSomeThing(anObject); } catch (SameException e) { // Do nothing or log, but don't abort current method. } try { doOtherThing(anObject); } catch (SameException e) { // Do nothing or log, but don't abort current method. } // ... some more calls to different method ... try { finallyDoYetSomethingCompletelyDifferent(anObject); } catch (SameException e) { // Do nothing or log, but don't abort current method. } 

As you can see, several different methods are called with the same object, and for each call the same exception is caught and handled the same way (or very similarly). An exception is not thrown, but can only be logged and then thrown.

The only reason there is a try-catch around each individual method is to always execute all methods, regardless of whether the previously executed method has completed.

I don't like the code above. It takes up a lot of space, is very repeatable (especially the catch -block entry is not presented here) and looks just bad.

I might think of other ways to write this code, but I don't really like them. The following occurred to me:

Case sequence / paradigm for case

(See Wikipedia or The Daily WTF )

 for (int i = 0; i <= 9; i++) { try { switch (i) { case 0: doSomeThing(anObject); break; case 1: doOtherSomeThing(anObject); break; // ...More cases... case 9: doYetSomethingCompletelyDifferent(anObject); break; } } catch (SameException e) { // Do nothing or log, but don't abort current method. } } 

This is obviously bad code, very error prone and amateurish.

Reflection

Use reflection to get Method objects for methods to call and store them in a list in the order in which they should be executed. Then go through this list and call the method using anObject as soon as the parameter. The exception is handled inside the loop.

I don't like this approach, because errors (for example, typos in method names) appear only at runtime, and the Reflection API is somewhat talkative.

Functor

Create the Functor class as follows:

 private class Functor { void doStuff(MyObject object) throws SameException; } 

Then create a list of Functor objects that call the methods. Like this:

 List<Functor> functors = new ArrayList<Functor>(); functors.add(new Functor() { @Override public void execute(MyObject anObject) { doSomeThing(anObject); } }); functors.add(new Functor() { @Override public void execute(MyObject anObject) { doOtherSomeThing(anObject); } }); 

Repeat this list later and call execute() for each Functor object. I can summarize my opinion on this approach in two words: "Inflate the code."


As I do not really like all four approaches, I would like to discuss this issue here. What do you think is the best approach? How did you solve such problems in the past? Maybe a much simpler solution that I missed completely?

+7
java exception
source share
8 answers

I would advocate a refactoring approach (or "Why did we come here first?"):

See why individual methods can throw an exception after running stuff with myObject, and this exception can be safely ignored. Since the exception exits the method, myObject must be in an unknown state.

If it is safe to ignore the exception, surely this should be the wrong way to report that something went wrong in each method.

Instead, perhaps this is every method that should do some logging on failure. If you do not use static logs, you can pass the logger to each method.

+6
source share

The functor approach is most enjoyable for me - it’s just a shame that Java has no better way to represent closures or delegates. This is basically what you really are, and in C # (and many other languages) it would be trivial.

You can slightly reduce physical bloat by using something like:

 Functor[] functors = new Functor[] { new Functor() { @Override public void execute(MyObject anObject) { doSomeThing(anObject); }}, new Functor() { @Override public void execute(MyObject anObject) { doSomeOtherThing(anObject); }} }; 

The dips here can be very different from the style guide you use, but I think it makes the code easier to read, as you can easily see the meat.

Better start lobbying closure in Java 8;)

+5
source share

At first glance, I agree with PeterR: if it is safe to ignore the exception, which is easy, perhaps this method should not throw an exception at all.

However, if you are sure that exactly what you want, say, maybe you are working with methods from a third-party library that insist on throwing certain exceptions, I would choose the following approach:

  • create an interface containing all the methods that can be called:

          
     public interface XyzOperations {
          public void doSomething (Object anObject);
          public void doOtherThing (Object anObject);
          ...
          public void finallyDoYetSomethingCompletelyDifferent (Object anObject);
    
  • create a default implementation class for these methods by appropriate methods, it is possible to refactor them from another place:

         public class DefaultXyzOperations implements XyzOperations {
         ... 
         }
    
  • use the Java Proxy class to create a dynamic proxy server on XyzOperations that delegates all the DefaultXyzOperations methods but will have centralized exception handling in the InvocationHandler . I did not compile the following, but this is the main outline:

       XyzOperations xyz = (XyzOperations) Proxy.newProxyInstance (
             XyzOperations.class.getClassLoader (),
             new Class [] {XyzOperations.class},
             new InvocationHandler () {
                 public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {
                      try {
                           method.invoke (new DefaultXyzOperations (), args);
                      }
                      catch (SameException e) {
                          // desired exception handling
                      }
                 }
             });
    
  • use this instance of the proxy from now on, just calling the methods you need

Alternatively, you can use AspectJ or a similar AOP solution to add tips on all XyzOperations methods and make exception handling there.

If you prefer to introduce a new dependency or write a proxy manually, it depends on your personal preferences and the total amount of code in which you need this behavior.

+3
source share

I agree with PeterR. I find it hard to believe that you really want to continue executing something after the exception has been thrown. If so, then something exceptional probably didn’t actually happen.

In other words, exceptions should not be used for logging or flow control. They should be used only when something exceptional has happened that the code at the level where the exception was thrown cannot deal.

That way, I would internalize the logging messages and remove the excluded exceptions.

At a minimum, I think you need to go back and understand what the code is trying to do and what business values ​​or rules are being implemented. As Peter R. said, try to understand: "Why did we come here first?" part of the code and what exactly mean exceptions.

+2
source share

Are the methods you call under your control? If so, then in this special case, returning error codes (or objects) may lead to a better overall design than using exceptions:

 handle(doSomething(anObject)); handle(doOtherThing(anObject)); // some more calls to different methods handle(finallyDoYetSomethingCompletelyDifferent(anObject)); 

from

 private void handle(ErrorCode errorCode) { // Do something about it } 

and

 private ErrorCode doSomething(Object anObject) { // return ErrorCode describing the operation outcome } 

It seems less detailed, though not dry.

Alternatively, you use some AOP mechanism to intercept doSomething , doOtherThing and finallyDoYetSomethingCompletelyDifferent using the Around Advice, which first handles and then removes the exception. Combine this with RuntimeExceptions and a pointcut, based on some nice descriptive annotation, and you'll perfectly understand what seems to be some kind of hidden issue related to cross-references.

I admit that I like the approach of Functor, tho.

EDIT: Just looked at your comment on one of the answers. In this case, I will probably go with AOP.

+2
source share

Leaving aside the refactoring issue, AspectJ (or similar) seems to be the easiest way to transparently catch / report these exceptions. You should be able to configure it to poke try / catch around method calls, even if you add new ones (the code block above, I suspect, will be quite fragile in the face of someone modifying this code, without fully understanding the rationale for this)

+1
source share

If you intend to use a different code style style, I would stick with a simple one:

 try { doSomeThing(anObject); } catch (SameException e) { Log(e); } try { doOtherThing(anObject); } catch (SameException e) { Log(e); } // ... some more calls to different method ... 

Update: I do not see how a transition with syntax similar to the Functor method reduces any of the involved codes. As John mentioned, java does not support simple syntax to reduce it. If it were C #, you can make a lot of variations around the whole fact that there is no special syntax for compiling such methods, for example expressions, for example actions.Add (() => doSomething (anObject));

0
source share

Let me elaborate on a functional approach ...

Depending on the complexity of your application, sometimes it’s worth moving a part of your business logic to a conf file, where it can be expressed more adequately and succinctly. Thus, you separate technical information (creating / calling a function, handling exceptions) from business logic - determining which methods and in which order should be called.

In its simplest form, it could be something like this:

 mypackage.Action1 mypackage.Action2 ... 

where ActionX is the class that implements the Functor class (or interface).

0
source share

All Articles