Checked Exceptions and Alternate Business Flows

July 17, 2007 – 12:57 | java

As I have always had problems with Java's checked exceptions, I had a little debate with Debasish in the comments section of his recent blog post regarding exceptions in DDD. Over there, I concluded my case with:

I also realize that you feel stronger urge to enforce exception handling in contracts than I do, because of another philosophical difference - I think things like "insufficient balance" aren't really exceptions, and should be expressed as the status of an account or a transaction in a first-class domain object.

But I guess that's an even finer line to draw, so, another debate for another day... :-)

Well, I guess that "other day" has come.:-)

Basically Debasish is promoting the approach to handle alternate business flows with checked exceptions, like so (sample code quoted from his post):

JAVA:
  1. Position updatePosition(..) {
  2.     try {
  3.  
  4.       //.. normal flow
  5.       current = getPosition(..);
  6.       current.doUpdate(..);
  7.       return current;
  8.  
  9.     } catch (ShortPositionException spex) {
  10.       //.. short position handling
  11.     } catch (DelinquentPositionException dpex) {
  12.       //.. delinquent position handling
  13.     }
  14.   }

Now, to me, that begs for the question: how is that, in essence, different from doing:

JAVA:
  1. public interface PositionStatus {}
  2. public class ShortPositionStatus implementing PositionStatus {};
  3. public class DelinquentPostitionStatus implementing PositionStatus {};
  4. public class ItsAllCoolPositionStatus implementing PositionStatus {};
  5.  
  6. // ... and then
  7.  
  8. Position updatePosition(..) {
  9.       current = getPosition(..);
  10.       PositionStatus status = current.doUpdate(..);
  11.       if (status instanceof ItsAllCoolPositionStatus) return current;
  12.       else if (status instanceof ShortPositionStatus)  // short position handling.
  13.       else if (status instanceof DelinquentPosition)  // delinquent position handling.
  14. }

I believe doing unnecessary type-based branching like that is generally considered very un-OO. I probably wouldn't hire myself if I wrote code like that in an interview. Why, then, should it be considered a legitimate pattern simply after being dressed up in a try-catch block?

Of course, I hear the argument that "the compiler then enforces the handling for you". And that would bring it back to my original debate with Debash, in which my main point was that the way checked exceptions are designed in Java to "help" enforcing contracts is highly intrusive - quoting my own comments from Debasish's blog:

"I would like to force the caller of my api..." that's the philosophy I'm against really :-) - checked exception is just the expression of that philosophy on the surface.

And the reason is very simple - I can't find any software engineering doctrine that says the callee mandating the caller's logical flow is a good thing. Imagine a language where an API can explicitly define rules for its callers like "you must have an alternate flow to deal with my return value if it's 0,1,or 2." That would sound crazy, but it's essentially the same as checked exceptions in Java.

Other languages learned from Java's mistake - C# exceptions are all unchecked, for instance.
...
I absolutely agree with you that exceptions are part of the contract. However, IMHO, an API contract should _not_ dictate implementation - either way for that matter. That an exception is part of the contract only goes as far as saying "be _prepared_ to handle this exception," but not "you _must_ handle this exception right here right now."

Again let's use the return value analogy - it's perfectly fine for a contract to say "I might return null so don't be surprised when you see one," but it's rather excessive to say "you must have an if testing my return value and do something differently if it's null."

Trackback from your site, or follow the comments in RSS.

Post a Comment