Fog Creek Software
Discussion Board




Exceptions Handling (OOP)

Hello everyone,
There is a fiery flame in our team in regard to the way the exceptions should be handled at the moment.

The primary question is whether every layer in a multilayered system should wrap an exception from the bottom into it's own exception before allowing it to propagate higher in the stack?

The problem is the current coding strategy is that application code, which actually can trake some actions, is layed on top of multiple-level service calls.
Application calls a indexed container, container calls streaming system. The latter fails. Should the container intercept the stream exception? It can do nothing about it - only throw it's own exception. And certainly it has much less knowledge on disk failures than a higher level program does.

There are two opposite poles in the discussion:
1. The exceptions provide a way for the object to throw an error information as far as it needed - until someone competent catches and deals it.
Therefore the container should not intercept the messages out of it's scope. It should raise only it's own exceptions, such as bounds checking failures, attemtps to remove an element from empty container, etc.
2. The exceptions are merely extension to the method return value allowing to pass the operation status instead of the result. In this case every method calling some external object's methods should carefully catch all the exceptions thrown from there and throw the other ones, possibly wrapping in the original ones.

Thank you in advance. It's a great pleasure to read such an intelligent posts.

P.S.: I am referring to the OOP exceptions, in the sense of Delphi, C++, and Java. Not the Win32 structured exceptions.

Sinclair Evilguest
Sunday, January 27, 2002

What does the customer think?

Sounds rather like a tempest in a teacup, if you ask me.  If I had it my way, I wouldn't bother wrapping the exception.  This may just be my knee jerk reaction to hearing the phrase "let's just wrap it", but look at it this way: it's added and useless code, it may be buggy, you might not catch every possible exception, this trick doesn't provide any new useful information, and in fact destroys information about where the exception occurred which may be invaluable later when you're debugging -- especially when a customer report about an exception comes in from the field.

I say this much more outweighs any cosmetic benefit that your catch-and-rethrow trick might provide.  What?  You don't think developers understand stack traces?  Too confusing a concept for them?

Exceptions are NOT the same as return values.  In fact, the reason they were invented was to eliminate this very sort of pathology of having to check every function call that could cause errors.  Treating them just like return values fundamentally goes against their raison d'etre.

http://www.alyosha.net/

Alyosha`
Monday, January 28, 2002

Thanks a lot!
This is exactly how I understand the exceptions purpose.
I am happy that the Great Gurus agree on this :)

BTW we do not have any ability to get the user interaction in our application. The exception handling is intended to, er, handle the error conditions that may occure.
Therefore it's not enough to display the exception Message to the user. Therefore it's crucial to pass enough information for the upper-level code to fix the problem.

Sinclair Evilguest
Monday, January 28, 2002

While it seems that your specific case is an exception, I generally prefer wrapping exceptions. It's a question of loose vs tight coupling, and tight coupling has burned me far too many times.

If you propagate an exception between application layers, then the client layer becomes dependant on whatever code is the ultimate source of the exception. One of the main reasons for OO encapsulation is to shield clients from having to know what's going on inside an object - if the object changes the way it does things, the client doesn't also have to be changed, and the application as a whole becomes easier to maintain.

A story that is rather close to (my) real life, imagine the maintenance nightmare when in stage one of your Java application you're storing a lot of data in flat files (causing IOExceptions), but for stage two you switch to EJBs (which cause NamingExceptions, FinderExceptions, CreateExceptions and RemoteExceptions), then, finding that EJBs are just too heavyweight, you switch to direct database calls (which cause JDBCExceptions).

If you haven't properly encapsulated (wrapped) the exceptions, then these changes mean you have to refactor all your client code on top of the changes you're making to the internals of the serving layer. Every change you make has tendrils that run through your entire codebase, and every extra class you have to touch is another possible source of bugs.

The EXCEPTION to this rule, is when you're writing a layer that only really exists to provide a bridge between two other layers. This seems to be what you're describing - an Application that talks to a Container that talks to the Streaming System.

If the sole purpose of the Container is to allow the Application to talk to the Streaming System, and the identity of the Streaming System isn't ever going to change, then the Container should be as hands-off as possible. It should only really be a way for the Application and Streaming System to be able to send messages to each other, it knows nothing about the context of those messages, so it should really interfere with them as little as possible (whether they be method calls, return values or exceptions).

Charles Miller
Monday, January 28, 2002

If you wrap your exception and rethrow it, you'll add some call stack information which will help locating bugs. On the other hand, if you throw away any information in the process of wrapping, you're just making matters worse. There are supposedly tools which can provide detailed call stack information using just an exe and a map file, so if you could get that to work, you'd be good to go. I couldn't.

Except for modules which turn out to have many hard-to-track excetions, wrapping exceptions seems like a waste of lines of code - unless maybe if you're programming in Java and dependency problems arise, which I have never had any of using Delphi. In real life, since there usually isn't much you can do with an exception except clean up and gracefully abort the operation that failed somewhere down the chain, the exact type of exception rarely matters, except for error logging/reporting purposes at the very top of the chain.

Johannes Bjerregaard
Monday, January 28, 2002

It mainly depends on what "face" you want the API to present.  Does it make sense for a method to throw a particular exception?

Usually, if you're propagating an exception for the user to handle ("user" is whoever uses your API), the user already gave enough information to deal with the problem.  For example, if you propagate Java's FileNotFoundException, the user probably gave the filename.  So clearly the API should send that unwrapped exception.

Information-hiding doesn't have to be absolute.

On the flip side, sometimes it should be.  This topic I think takes a bit of aesthetics, and an understanding of what the user wants to see.

Ringo
Tuesday, January 29, 2002

I agree with the "loose coupling" example cited above.
I can't count the number of projects I've worked on when hours upon hours have been spend discussing, arguing about and implementing and exception handling strategy.

Coose the simplest, easiest to implement method, especially if its a business application. To me the crucial things are:

Providing an elegant "get out" for the user.

Logging the user/machine/error/time/details to somewhere where the developer can view it remotely.

Spend more time making sure that errors wo'nt happen rather than what to do if they do!

Tony
Tuesday, January 29, 2002

Use option 2.

If your subordinate has a problem, he should take it up with you, not goto your boss.

James Ladd
Tuesday, January 29, 2002

Actually, I prefer to stick with a single Exception class with a free-form text field, throughout all the layers of code.

At the point where the error occurs, my code records the error in a log file and throws the exception. The higher-level code catches the exception and the user interface tells the user that a technical problem occurred.

With that approach, there's no benefit to having multiple exception classes. It's better to spend time making the code more robust so errors don't occur as often in the first place.

Jared Levy
Tuesday, January 29, 2002

Great thanks to everyone answered so far.
I can see that usually exceptions are not used to serve the purpose they were designed for.
I'd like to note that displaying the proper user message is not a purpose in our project. The default methods are well appropriate for such kind of "handling". For example, Delphi simply displays the message box to the user and goes on pumping the message queue.
We are developing some kind of the embedded application. Therefore it is crucial that it handles most error conditions carefully, trying to operate under any circumstances.
To illustrate my point of view consider some imaginary simple network application. (I am not going to describe our exact application because that would provide too much unneccessary detail for you).
Now here is three objects: object 1 (Socket) encapsulates the network connection; object 2 (Codec) performs the data compression/decompression, and object 3 is the main program. The main program creates both other objects and sets the Socket as an input for the Codec.
Now while main program tries to pump out the decompressed data the connection gets lost due to some external reason.
The Socket object has the detailed information on the error condition. However, it does not have enough knowledge to handle this situation, so it simply reports it to the caller in form of the exception. The Codec object knows nothing about the network. It allows the exception to propagate further up the stack.
Now we can teach the main program to handle such conditions. For example, it can try to reconnect, or go to another address from it's internal list, or something else. It needs some information on the error condition to perform it's job. Actually, in this case it does not matter too much what was the call stack. This information is useful for the developer if the application encounters division by zero, so he can find thу bug. But the connection losing is not a developer mistake, it is a reality.
Here only the error nature is important - all connections will be processed the same way.
In this particular case the Codec should never wrap the exception from the external object, else the main program won't be able to handle the error properly. Even if the reraised exception aggregate the original one, the natural procedure of matching the exception class with a proper handler will be interrupted, and the special code will have to be written to replace it.

The problem is that my opinion is of a theroretic nature. Some things tend to be done in the ways other than described in original manuals. So I am seeking for the advices from the experienced developers who already adopted some successful patterns of making robust applications using the exception handling technique.

Thank you very much again for all the responses. You help me to achieve an understanding of the best programming practices.

Sinclair Evilguest
Wednesday, January 30, 2002

Seems to me in your example, that there should be something between the codec and the Socket that does socket management.  That way, if the connection gets dropped, it can attempt to reconnect.  If you don't want to change your code, you can use a remote proxy type pattern.

On the other had, if the codec can't deal with the exception, pass it on.  Document what kind of exception it can throw (I assume this is not Java Code) and let the next level deal with it.  That will casue a full resend of the information.  Or go Asynchronous (MQSeries, JMS) and ignore, if you can.

It really depends on how often you expect network problems.  If it is only inside a data center, than it probably is not somthing that is going to be self correcting.  OTOH if it is across the internet, your best bet may be just to retry.

Adam Younga
Wednesday, January 30, 2002

To quote the example above:

".... The main program creates both other objects and sets the Socket as an input for the Codec. "

I think this is an important part of making the decision when to wrap and when not to wrap. Consider this: In the example, main knows about Sockets, and it knows about Codecs. Therefore, it knows the interfaces of both, and can be expected to know about the exceptions that can get thrown from both.

Consider it slightly differently - what if main just set up a codec, and doesn't handle the socket itself? In that case, I'd argue for a wrapper exception, since the interface to the codec doesn't say anything about sockets, so the caller shouldn't need to know how to handle specific socket errors.

I've found this a useful rule of thumb.

Chris Tavares
Wednesday, January 30, 2002

When I first saw Exceptions my thought was: "great rather than solving problems top down, now I may solve them bottom up".

Definitively: option 2: solve the problem at the nearest point and reraise only if you can't help at that level.

Isn't it part of the OOP logic? The object is self-contained, it has to care about its problems on its own if somehow possible.

Matheus Moeller
Thursday, February 07, 2002

*  Recent Topics

*  Fog Creek Home