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):

  class A {
    util() { return true }
    algo() {
      if (util()) { /* do some stuff */ }
      else { /* do different stuff */ }
    } 
  }

  class B extends A {
    util() { return false }
  }

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.

Try a slightly more complex case:

  class C {
    boolean state = true
    setState(x) { state = x }
    util() { return state }
    algo() {
      if (util()) { /* do some stuff */ }
      else { /* do different stuff */ }
    }
  }

  driver(C c) {
    c.setState(false)
    c.algo()
  }

Class C looks quite complete in itself, and your unit tests of class C could reach every line. Nothing up my sleeves!

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.

2 thoughts on “Bad uses of Template Method”

  1. Infact, I think you are being too mild. I say: avoid the template pattern like the plague, as it usually breaks encapsulation and does not offer enough flexibility.

    Instead, use the strategy pattern (if you have to use Java, that is…).

  2. The “know it by name” comment hints at my real opinion which came out over lunch here in the office: “Template method is a seductive demon, know it by name, and cast it out of your code.”

Leave a Reply

Your email address will not be published. Required fields are marked *