Run-time Type Checks Are Harmful

How To Use Your Language's Type System To Remove Redundant Code

© James Huw Evans

Programming Languages Article, (C) Huw Evans

What you can do to ensure your programs are more reusable by not performing run-time type checks on objects before calling type-specific code.

Programmers who are new to object-orientation are sometimes unaware that by using the dynamic dispatch facilities of the language they can reduce the amount of code they have to write and maintain. A particular anti-pattern (Anti-Patterns Home Website, Wikipedia Article) is the use of a tree of if-then-else statements to test an object's type at run-time so that the correct piece of code can be called. This article uses C#, but the discussion also applies to Java.

Spotting the Anti-Pattern

Spotting this anti-pattern is easy, just look for lists of run-time type checks:

private static void processObject(Object o)
{
if (o is A)
doSomethingWithA(o as A);
else if (o is B)
doSomethingWithB(o as B);
}

Types A and B are not related, i.e., they don't share a supertype (except Object, which isn't useful to us in this case). The parameter 'o' is tested to see if it is of type A and if it is, 'o' is cast to type A before doSomethingWithA is invoked.

The Consequences of the Anti-Pattern

The disadvantage with this approach is that the above code has to be edited if a new type is introduced. The addition of type C would require a new 'if' statement and method to be written and tested, requiring processObject to be retested as well. As the doSomething methods are not defined as part of the types they manipulate the engineer has extra work to do. They have to pass more parameters to their methods (the reference to 'o'), and the code in the doSomething method is likely to be more verbose than equivalent code defined as part of the type (because code defined outside the type has to perform calls on it to access the data it wants to process). The engineer that writes the above code also has to decide what to do if 'o' is neither A nor B, so processObject isn't type-safe.

processObject is just "object management" code, unnecessary program overhead that helps the engineer track what to do with their objects at run-time. However, this is the job of the language's type system. Doing it manually in this way makes re-use much harder as information on what to do with a given program type has been explicitly coded.

Refactoring the Anti-Pattern

The reason that the engineer has decided they need to test the type of the object at run-time is because its possible types (A and B) share no useful supertype. To refactor this anti-pattern, the first step is to relate the two types through a common interface type (CallInterface) which defines a single method 'call':

interface CallInterface
{
void call();
}

The implementation of A's call method then becomes whatever was in its doSomething method. To call the right method, we can then do this:

CallInterface callable = new A();
callable.call();

and the correct call method will be invoked, depending on the run-time type of callable (either A or B). As the language ensures that the correct call method will be invoked, we can completely remove the processObject method. To add type C, we have it implement CallInterface, create one at run-time and just invoke its call method. The above code will work with any type as long as it implements CallInterface. We can then improve the above two lines of code to abstract over the type of object we are creating by using the factory pattern (Factory Method Overview, Wikipedia Article).

Conclusions

Engineers need to check that they don't write code that can be removed by making effective use of the language's type system. Using the type system in this way reduces the amount of code that has to be written, tested and maintained.


The copyright of the article Run-time Type Checks Are Harmful in Computer Programming is owned by James Huw Evans. Permission to republish Run-time Type Checks Are Harmful must be granted by the author in writing.


Programming Languages Article, (C) Huw Evans
       


Post this Article to facebook Add this Article to del.icio.us! Digg this Article furl this Article Add this Article to Reddit Add this Article to Technorati Add this Article to Newsvine Add this Article to Windows Live Add this Article to Yahoo Add this Article to StumbleUpon Add this Article to BlinkLists Add this Article to Spurl Add this Article to Google Add this Article to Ask Add this Article to Squidoo