custom exception

Derive Specific Exceptions

 

OK, where have we gotten up to with our journey into Exception Management? 

We’ve seen the problems associated with throwing raw System.Exceptions, and that we should favour throwing custom exceptions. We’ve learned how we might want to name our custom exceptions here and here.

Now that we have well-named custom exceptions, how do we catch them? 

Often the place where we want to manage errors is at an application boundary. Here, we convert exception content into an error message for the user to see or for a client system to receive. This application boundary will often need to handle many different types of custom exceptions. Depending on the system’s size and complexity, there could be 10s to 1,000s of custom exceptions, each for a specific error condition. Correspondingly, catch blocks could become rather long:

  catch (MissingCustomerRegistration)
  {
     // Handle MissingCustomerRegistration exception
  }
  catch (InvalidCustomerFirstName)
  {
     // Handle InvalidCustomerFirstName exception
  }

  // ... 27 more validation exceptions!

  catch (DuplicateCustomerEmailAddress)
  {
     // Handle DuplicateCustomerEmailAddress
  }

Every time we have a new exception, we would need to add another catch clause—not good. Such an implementation would go against the spirit of the Open-Closed Principle (OCP). The OCP states that we should build our classes to open them for extension but close them to modification. Specifically, we want a design where we can add a new exception without needing another catch block. 

OK. But how do we achieve that?

First, notice how all the custom exceptions in our example have to do with failed input data validations. 

How about we create an abstract base InputDataException and derive our concrete custom exceptions from it? 

Here goes:

  public abstract class InputDataException : System.Exception
  {
     // An abstract exception for invalid input data.
     // This exception exists to ring-fence invalid input data
     // custom exceptions.
     // It's abstract—we don't want to create this exception directly.
  }

  // Specific custom exceptions
  public class MissingCustomerRegistration : InputDataException
  {
     // ...
  }
  public class InvalidCustomerFirstName : InputDataException
  {
     // ...
  }
  public class DuplicateCustomerEmailAddress : InputDataException
  {
     // ...
  }

Now we can catch and handle invalid input data exceptions in a single catch block for the abstract base exception InputDataException:

  catch (InputDataException ex)
  {
     // Handle InputDataException
  }

Note: This cute exception subsystem will only work if all the specific input data exceptions operate in the same way. It will fail, for example, if some have error messages that we can show to users while others do not. As so often in programming—Consistency is Key.

We can expand this exception handling mechanism further, and we will do so soon. 

Deriving specific exceptions from an abstract exception type, named for a cohesive collection of error conditions (e.g. invalid input data), is an excellent design choice.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply