Skip to content Skip to sidebar Skip to footer

Condition Checking Vs. Exception Handling

When is exception handling more preferable than condition checking? There are many situations where I can choose using one or the other. For example, this is a summing function whi

Solution 1:

Exceptions are much more manageable, because they define general families of things that can go wrong. In your example there is only one possible problem, so there is no advantage to using exceptions. But if you had another class that does division, then it needs to signal that you can't devide by zero. Simply returning None wouldn't work anymore.

On the other hand, exceptions can be subclassed and you can catch specific exceptions, depending on how much you care about the underlying problem. For example, you could have a DoesntCompute base exception and subclasses like InvalidType and InvalidArgument. If you just want a result, you can wrap all computations in a block that catches DoesntCompute, but you can still do very specific error handling just as easy.

Solution 2:

Generally, you want to use condition checking for situations which are understandable, expected, and able to be handled. You would use exceptions for cases that are incoherent or unhandleable.

So, if you think of your "add" function. It should NEVER return null. That is not a coherent result for adding two things. In that case, there is an error in the arguments that were passed in and the function should not attempt to pretend that everything is okay. This is a perfect case to throw an exception.

You would want to use condition checking and return null if you are in a regular or normal execution case. For instance, IsEqual could be a good case to use conditions, and return false if one of your conditions fails. I.E.

function bool IsEqual(obj a, obj b)
{ 
   if(a isnull) returnfalse;
   if(b isnull) returnfalse;
   if(a.Type != b.Type) returnfalse;

   bool result = false;
   //Do custom IsEqual comparison codereturn result;
}

In that scenario, you are returning false both for the exception cases AND the "objects are not equal case". This means that the consumer (calling party) cannot tell whether the comparison failed or the objects were simply not equal. If those cases need to be distinguished, then you should use exceptions instead of conditions.

Ultimately, you want to ask yourself whether the consumer will be able to specifically handle the failure case that you encountered. If your method/function cannot do what it needs to do then you probably want to throw an exception.

Solution 3:

If you're asking, you should probably be using exceptions. Exceptions are used to indicate exceptional circumstances, a specific case where things work differently from other cases. This is the case for prettymuch all errors and for many other things as well.

In your second implementation of sum_, the user has to check every single time what the value was. This is reminiscent of the C/Fortran/other-languages boilerplate (and frequent source of errors) where error codes go unchecked that we avoid. You have to write code like this at all levels to be able to propagate errors. It gets messy and is especially avoided in Python.

A couple other notes:

  • You often don't need to make your own exceptions. For many cases, the builtin exceptions like ValueError and TypeError are appropriate.
  • When I do create a new exception, which is pretty useful, I often try to subclass something more specific than Exception. The built-in exception hierarchy is here.
  • I would never implement a function like sum_, since typechecking makes your code less flexible, maintainable, and idiomatic.

    I would simply write the function

    defsum_(a, b):
        return a + b
    

    which would work if the objects were compatible and if not it would already throw an exception, the TypeError that everyone is used to seeing. Consider how my implementation works

    >>>sum_(1, 4)
    5
    >>>sum_(4.5, 5.0)
    9.5
    >>>sum_([1, 2], [3, 4])
    [1, 2, 3, 4]
    >>>sum_(3.5, 5) # This operation makes perfect sense, but would fail for you
    8.5
    >>>sum_("cat", 7) # This makes no sense and already is an error.
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 1, in sum_
    TypeError: cannot concatenate 'str' and 'int' objects
    

    My code was shorter and simpler yet is more robust and flexible than yours. This is why we avoid typechecking in Python.

Solution 4:

My main reason for preferring exceptions to status returns has to do with considering what happens if the programmer forgets to do his job. With exceptions, you might overlook catching an exception. In that case, your system will visibly fail, and you'll have a chance to consider where to add a catch. With status returns, if you forget to check the return, it will be silently ignore, and your code will continue on, possibly failing later in a mysterious way. I prefer the visible failure to the invisible one.

There are other reasons, which I've explained here: Exceptions vs. Status Returns.

Solution 5:

Actually, the problem of using exceptions lies in the business logic. If the situation is exception (i.e. should not happen at all), an exception can be used. However, if the situation is possible from the business logic point of view, when it should be handled by conditional checking, even if this condition looks much more complicated.

For example, here is the code that I met in the prepared statement, when the developer is setting parameter values (Java, not Python):

// Variant A
try {
  ps.setInt(1, enterprise.getSubRegion().getRegion().getCountry().getId());
} catch (Exception e) {
  ps.setNull(1, Types.INTEGER);
}

With conditional checking this would be written like this:

//VariantBif(enterprise!=null&&enterprise.getSubRegion()!=null&&enterprise.getSubRegion().getRegion()!=null&&enterprise.getSubRegion().getRegion().getCountry()!=null) {
  ps.setInt(1, enterprise.getSubRegion().getRegion().getCountry().getId()); 
} else {
  ps.setNull(1, Types.INTEGER);
}

Variant B seems much more complicated from the first sight, however, it is the correct one, since this situation is possible from the business point of view (the country may not be specified). Using exceptions will cause performance issues, and will lead to misunderstanding of code, since it will not be clear, is it acceptable for the country to be empty.

Variant B can be improved by using auxiliary functions in EnterpriseBean that will return the region and country immediately:

public RegionBean getRegion() {
  if (getSubRegion() != null) {  
    return getSubRegion().getRegion();
  } else {
    returnnull;
  }
}

public CountryBean getCountry() {
  if (getRegion() != null) {
    return getRegion().getCountry();
  } else {
    returnnull;
  }
}

This code uses something like chaining, and each get method seems simple enough and is using only one predecessor. Therefore variant B can be rewritten as follows:

// Variant Cif (enterprise != null && enterprise.getCountry() != null) {
  ps.setInt(1, enterprise.getCountry().getId());
} else {
  ps.setNull(1, Types.INTEGER);
}

Also, read this Joel article about why exceptions should not be overused. And an essay by Raymon Chen.

Post a Comment for "Condition Checking Vs. Exception Handling"