Bad uses of Template Method

Template Method is one of the original GoF patterns – an obvious way to use inheritance to share functionality, but I’d hazard it’s more risk than it’s worth. Here’s an stripped-down example (in pseudocode):

Parent class A has a function algo() which uses another function util() to tune its behaviour. Derived class B provides an alternative version of util(), so an instance of class B calling algo() uses the implementation of algo() in class A but uses the util() provided in class B.

There’s some obvious problems in the way I’ve written this: algo() has two modes of behaviour but you can only access one of them through instances of class A. To get the other branch you must call algo() via an instance of class B. If you unit test thoroughly you’ll discover that you can’t test all of class A from its unit tests – that’s a bad smell. The function util() tells class A that it’s actually being run by an instance of class B – that smells too.

But maybe it’s not bad enough to discourage. After all, you can see that something extra is happening to get to the else branch of that if statement.

But perhaps you want to change the behaviour of algo() for a particular case; one very common solution is to introduce something like this:

class D extends C {
util(x) { return true }
}

Use driver() with an instance of class D and the code (which you didn’t touch at all) suddenly stops going down the expected path, even though you can see the explicit call to setState() in driver() telling to take the else branch!

Debugging this type of situation is very difficult. There’s nothing in class C or in driver() that vaguely hints at this changeable behaviour. It might not even occur to you that there is a class D. You’d probably start debugging by just staring despairingly at the code for class C.

Of course there are a bunch of strategies for solving this sort of puzzle, getting the flexibility and reuse but keeping the behaviour change out in the open. So try to recognise the pattern, know it by name, and develop an instinct for its risks.