Section 13.11.  Exception Hierarchies

Table of Contents

13.11. Exception Hierarchies

Use exception objects when two or more exceptions are related.

Another problem with using raw strings as exceptions is that string-based exceptions offer no easy way to create new and specialized forms of existing exceptions that existing code can still catch and handle.

Consider the string-based exception for reporting integers outside a given range, as shown previously in the "OO Exceptions" guideline:

    croak( "Numeric value $num too big (must be $MAX_ALLOWED_VALUE or less)" )
        if $num > $MAX_ALLOWED_VALUE;

Suppose you also need to provide a special version of that exception for reporting integers that are so big that they're outside the range that Perl can represent exactly:

    croak( "Numeric value $num waaaaay too big (must be $MAX_INT or less)" )
        if $num > $MAX_INT;

The original test for catching the string-based "big number" exception was:

    # If the candidate was considered too big, go with the maximum allowed...
    if ($EVAL_ERROR =~ m{\A Numeric [ ] value [ ] \S+ [ ] too [ ] big}xms) {

Unfortunately, that regex won't match the error message of this new exception, so the handler will completely ignore it. Unless, of course, it was changed to:

    # If the candidate was considered too big, go with the maximum allowed...
    if ($EVAL_ERROR =~ m{\A Numeric [ ] value [ ] \S+ [ ] (wa+y [ ])? too [ ] big}xms) {

But that change makes the code that catches these exceptions more complex, harder to read, and less maintainable. Worse still, you're also going to have to change every other similar regex anywhere else that the original exception was being caught.

In contrast, suppose you had originally used an object-oriented exception:

    croak( X::TooBig->new( {num=>$num, limit=>$MAX_ALLOWED_VALUE} )
        if $num > $MAX_ALLOWED_VALUE;

with a correspondingly object-oriented test in the exception handler:

    if ( X::TooBig->caught(  ) ) {

If you subsequently needed to also use exceptions of a more-specific (i.e., derived) class:

    package X::WaaaaayTooBig;
    use base qw( X::TooBig );
# [Implement variant behaviour here]

    # and later...
croak( X::WaaaaayTooBig->new( {num=>$num} ) ) if $num > $MAX_INT;

then your exception handler would have to become:

    if ( X::TooBig->caught(  ) ) {

That is, it would stay exactly the same and continue to work just fine with no modification whatsoever. And it would continue to work no matter how many other exception types you subsequently derived from X::TooBig.

This ability to decouple exception-handling code from the particulars of the exceptions it handles is probably the single most compelling reason to use object-oriented exceptions.

    Table of Contents
    © 2000- NIV