tags:

views:

304

answers:

8

In a 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 method are called with the exact same object and for every call the same exception is caught and handled the same (or in a very similar) way. The exception is not re-thrown, but may only be logged and then discarded.

The only reason why there is a try-catch around every single method, is to always execute all of the methods, no matter if a previously executed method failed.

I don't like the above code at all. It takes up a lot of space, is very repetitive (especially the logging done in the catch-block; not presented here) and just looks bad.

I can think of some other ways to write this code, but don't like them very much, either. The following options came to my mind:

Loop-switch sequence / for-case paradigm

(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 looks amateurish.

Reflection

Use reflection to get Method objects for the methods to call and store them in a list in the order they are supposed to be executed. Then iterate over this list and call the method using anObject as only parameter. The exception is handled inside of the loop.

I don't like this approach, as bugs (for example typos in the method names) only pop up during runtime and the Reflection API is somewhat chatty.

Functor

Create a Functor class like this:

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);
    }
});

Later, iterate this list and call execute() on every Functor object. I can summarize my feeling about this approach with two words: Code bloat.


As I don't really like all four approaches, I would like to discuss this problem here. What do you feel is the best approach? How did you solve similar problems in the past? Is there maybe a much simpler solution I missed completely?

+4  A: 

The functor approach is the nicest one to my mind - it's just a shame that Java doesn't have a nicer way of representing closures or delegates. That's basically what you're really after, and in C# (and many other languages) it would be trivial.

You can cut down on the physical bloat somewhat 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 whitespace collapsing here may well be against the style guide you're using, but I think it makes the code easier to actually read, in that you can see the meat more easily.

Better start lobbying for closures in Java 8 ;)

Jon Skeet
The bloat is even worse than that--you (and he) forgot the throws clause.
Michael Myers
Wow, you are fast! :)I also thought about closures/delegates. But, as you said, the most similar construct Java has to offer are functors.Maybe you are right about using a code formatting that is best for reading, but does not necessarily respect the style guide we use.
Tobias Müller
@mmyers In the original code the exception caught is a RuntimeException. So this is not a problem in this particular case. (In others it may be, of course.)
Tobias Müller
Style guides should be used as "pretty stern guides" rather than "absolutely concrete rules" IMO. Just occasionally, breaking the guidelines can make life a lot nicer. (switch/case can have this, if each case is a single return statement. Put each case/return on a single line.)
Jon Skeet
Another note: The @Override annotation isn't strictly necessary here. If the anonymous classes fail to implement the interface, they will fail to compile anyway. So that's a tiny bit less clutter.
Michael Myers
And another note: Functor here doesn't need to be a class, it should be an interface
Sandman
+5  A: 

I would advocate the refactoring approach (or "Why did we get here in the first place?"):

Consider why the individual methods can throw the exception after doing "stuff" with myObject and that exception can then be safely ignored. Since the exception escapes the method, myObject must be in an unknown state.

If it's safe to ignore the exception, surely it must be the wrong way to communicate that something went wrong in each method.

Instead, maybe it's each method that needs to do some logging on failure. If you don't use static loggers, you can pass a logger to each method.

PeterR
Unfortunately the original exception is of type ConstraintViolationException thrown by (I think) Hibernate. This can not be changed.We could catch and handle the exception in each method. But that would not make the code any better. Ideally I would like to only have one catch-clause for all calls.
Tobias Müller
But that would mean that nothing got written to the database, so why try and continue with the transaction? Depending on underlying database, it will already be marked for rollback.
PeterR
Unless you're taking the exception to mean "Object already exists in tabel, so we ignore failure on insert". The right way is to first try and load the object into Hibernates session cache, then execute save (or saveOrUpdate). I know because I've been there myself :)
PeterR
I'll definitely look into that. It currently works fine with Oracle and Derby. Our use case says that it's ok if some information could not make it into the database. Our input data can sometimes be quite flaky. Some of the later application are ok with it, though. We don't like that either.
Tobias Müller
We usually do this check ("does it already exist?"). I wonder why we don't do it in this case. Maybe just some precaution. I'll have to ask the original developer (and document it accordingly). But probably it's because "damaged" data is fine in this use case. Well...
Tobias Müller
+2  A: 

I agree with PeterR. I find it hard to believe that you really want to continue execution of something after an exception has been thrown. If you do, then something exceptional probably has not actually happened.

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

As such, I would internalize the logging messages and remove the exceptions that are being thrown.

At a minimum, I think you need to go back and re-understand what the code is trying to do and what business value or rules are being implemented. As PeterR said, try to understand "why did we get here in the first place?" part of the code and what exactly the exceptions mean.

Chris Johnston
+2  A: 

Are the methods you're calling under your control? If so, in this special case returning error codes (or objects) might yield to a better overall design than using exceptions:

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

with

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

and

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

This seems less verbose, although not DRY.

Alternatively, you use some AOP mechanism to intercept the calls to doSomething, doOtherThing and finallyDoYetSomethingCompletelyDifferent with an Around Advice that first handles and then discards the Exception. Combine that with RuntimeExceptions and a pointcut based on some nice descriptive annotation and you perfectly capture what seems to be some kind of hidden crosscutting concern.

I do confess that I like the Functor approach, tho.

EDIT: Just saw your comment on one of the answers. I would probably go with the AOP approach in that case.

springify
+3  A: 

On the first look, I agree with PeterR: if it is safe to ignore the exception that easily, maybe that method should not be throwing an exception at all.

However, if you're sure that's exactly what you want, say, perhaps you're working with methods from a 3rd party library which insist on throwing specific exceptions, I would opt for the following approach:

  1. 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);
    
  2. create a default implementation class for those methods appropriate methods, possibly refactoring them from some other place:

        public class DefaultXyzOperations implements XyzOperations {
        ... 
        }
    
  3. use a Java Proxy class to create a dynamic proxy on XyzOperations which would delegate all methods to DefaultXyzOperations, but, would have centralized exception handling in its InvocationHandler. I didn't compile the following, but it's a basic 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
                     }
                }
            });
    
  4. use that proxy instance from then on, simply calling the desired methods

Alternatively, you could use AspectJ or similar AOP solution to add around advice to all methods of XyzOperations and do the exception handling there.

Whether you would rather introduce a new dependency, or write proxies manually is up to your personal preference and the total amount of code where you need such behavior.

javashlook
+1  A: 

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

Brian Agnew
A: 

If you are going the different code style route, I would stick with a simple:

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

Update: I don't see how going with a syntax like the Functor's approach reduces any of the code involved. As mentioned by Jon, java doesn't support a simple syntax to reduce it further. If it were c# there are plenty of variations you can do, all around the fact that there isn't much extra syntax to compose methods like that i.e. an expression like actions.Add(() => doSomething(anObject));

eglasius
Others disagree, but I'm with you Freddy. The code does what is wanted of it, and it's only aesthetics and a desire to complicate things otherwise. If there were going to be hundreds of these calls, then perhaps Functors or a refactor. But at the moment, leave it alone.
Phil H
A: 

Let me elaborate on the functor approach a bit more...

Depending on the complexity of your app, sometimes it is worth to move part of your business logic to a conf file, where it can be expressed more appropriately and briefly. This way you separate the technical details (the functor creation/invocation, exception handling) from the business logic - the definition of which methods and in what order are to be called.

In the simplest form it could be something like this:

mypackage.Action1
mypackage.Action2
...

where ActionX is a class implementing the Functor class (or interface).

Bartosz Klimek