Friday, June 25, 2010

Do your work, or throw an exception - example

The last post gave an illustration of a employee telling his manager if he cannot do his work as usual instead of requiring his manager to ask him if he will start doing what is expected of him every time she asks him to.

Below is a code example of this idea that I posted on StackOverflow:

class GoodEmployee {
void DoWork() {
if (CarIsInTheShop)
throw new CantDoWorkException();
else
_DoWork();
}
}

class BadEmployee {
bool DoWork() {
if (CarIsInTheShop)
return false;
else
{
_DoWork();
return true;
}
}
}


Imagine using each of these. Using GoodEmployee means that client code can operate as normal. Wherever appropriate, _if_ appropriate, a try/catch _may_ be used to properly handle the exception, if that is even possible. The most appropriate try/catch may be in place already in a form something like this:


// main loop:
while(!timeToQuit) {
try {
REPL(); // read eval print loop, for example
}
catch (TransientException e) {
TellUserHeCantDoThatRightNow(e);
}
}


Meanwhile, client code of GoodEmployee may look like this:


myGoodEmployee.DoWork();


Contrast this with proper usage of BadEmployee:


if (!myBadEmployee.DoWork) {
return false;
}
...
return true;


Not _so_ bad. But what if we call DoWork in lots of places? What if there are not 1 but many different methods like DoWork that have to be called? Worst of all, what if we want to have N layers of indirection that must exit when the employee cannot do his work? What if we apply this same pattern (return error codes instead of throwing an exception) to those methods? Then we have something like this:


A() {
if (!B())
return false;
// do some other work;
return true;
}

B() {
if (!C())
return false;
// do some other work;
return true;
}

C() {
if (!myEmployee.DoWork1())
return false;
if (!myEmployee.DoWork2())
return false;
if (!myEmployee.DoWork3())
return false;
// do some other stuff
return true;
}


YUCK!

I have worked on an application like this. Making any minor little changes to it is an absolute pain because it employs the above pattern throughout. Contrast that with otherwise comparable apps that throws exceptions and allows exceptions to be thrown--the latter group of apps not only have less code, cleaner code, and fewer bugs, they also have _more robust_ exception handling.

I hope these clearly examples show how exception-phobia and speculative catch blocks are destructive and literally multiply the amount of code you have to maintain, as well as hide and even introduce bugs.

No comments:

Post a Comment