Refactoring: Replace Exception with Test
Exceptions are mechanism to transport information about errors in object-oriented code. But they come with performance hit when not used carefully. Still we find a lot of code when exceptions are not avoided but used as a control mechanism in code flow. This post introduces replace exception with test refactoring that helps us to avoid at least some exceptions.
Applying replace exception with test refactoring
The code that possibly needs replace exception with test refactoring looks like this in pseudo-code.
public bool DoSomething() {
try
{
// Do something
return true;
}
catch
{
return false;
} }
This is specially bad example as all possible exceptions are ignored. In many cases we can avoid these try-catch blocks by just adding some checks to make sure that given operation can be done.
public bool DoSomething() {
if(InvalidCondition)
{
return false;
}
// Do something
return true; }
NB! This refactoring is not silver bullet and please don’t try to apply it to all try-catch blocks in your code. It depends on situation if the refactoring is applicable to given part of code or not. Being too enthusiastic with this refactoring may end up with unstable and error-prone code. So, be warned!
Replace exception with test refactoring is described in detail in legendary refactoring book “Refactoring: Improving the Design of Existing Code” by Martin Fowler.
Example from practice
I worked on very bad codebase that used SharePoint server-side API-s to do some data migrations. There was method that copied field values from dictionary to list item. All exceptions were supressed (bug number one) and some bugs where not just avoided by simple checks. So, I started with code like this.
public void ImportDocumentMetaData(SPListItem item, IDictionary<string,object> values) {
foreach(var element in values)
{
try
{
item[element.Key] = element.Value;
}
catch {}
} }
Without expecting you to know much about SharePoint just consider list item there as a mix of database row and object. The code shown above goes brutally through all situations even when exceptions can be avoid.
For SharePoint list item field we can make the following checks:
- does list item have field with given name?
- is this field assignable?
- do we have correct value for the field?
Considering these checks as custom extension methods of SPListItem class we can write the code above like this, adding checks before real assignment is done.
public void ImportDocumentMetaData(SPListItem item, IDictionary<string,object> values) {
foreach(var element in values)
{
if(!item.CanAssignValue(element) || !item.IsValidValue(element))
{
continue;
}
item[element.Key] = element.Value;
} }
It doesn’t seem like big deal in code but it can change a lot. Specially when using in long loops or in methods that get a massive number of calls.
Wrapping up
I was importing 50 GB documents from very old SharePoint to newer one and the number of documents was few millions. The import process that seemed like taking forever ran smooth within 20h after applying this refactoring. Exceptions are costly and ignoring this fact may come with bad consequences. Replace exception with test is refactoring to use in these cases.