Some thoughts on TDD and the Strategy pattern

published: Sat, 11-Jun-2005   |   updated: Mon, 8-Aug-2005

At one of the Birds of a Feather sessions at Tech*Ed, someone said something to the effect that you aren't doing TDD until you understand the Strategy pattern. That was both fascinating and coincidental since I was only recently contemplating the use of the Strategy Pattern (or the Policy pattern as it's sometimes known) in some OO design work.

Let's recap. The Strategy pattern is essentially a plugin pattern. Assume you have a class that wants to use some behavior, say persisting an object. In the old days you'd add a method that did something like this:

public void Save() {
  if (target == PersistenceTargets.XML) {
    //  write the this object to XML
  }
  else if (destination == PersistenceTargets.INI) { 
    // write the this object to an INI file
  }
  else if (destination == PersistenceTargets.Database) { 
    // write the this object to a database table
  }
  ..etc..
}

Now, as you can readily appreciate, this is a strong code smell, but it's not obvious what to do about it. The answer is an application of the Strategy pattern. First we define an interface

public interface IPersistenceProvider {
  void PersistObject(object o);
}

Then we write a set of implementations of this interface:

public class XMLTarget : IPersistenceProvider {
  void PersistObject(object o) {
    .. persist o to an XML stream ..
  }
}

public class INITarget : IPersistenceProvider {
  void PersistObject(object o) {
    .. persist o to an INI file ..
  }
}

..etc..

Then we can rewrite the original Save() method like this:

public void Save() {
  target.PersistObject(this);
}

(Obviously we've assumed that the target field is set in the same place as the original enum field.) We've essentially got rid of a yucky-looking quasi-switch mega-if statement and used the language's syntax to hide it in a polymorphic class model. Welcome to the Strategy pattern: it gets rid of yucky if statements, amongst other things.

If you think about it, fake or mock objects are a prime example of using a Strategy pattern; hence the original speaker's comment. A mock object is an implementation of an interface that is written explicitly for testing. It is not the real heavy-weight thing. And if you are writing code TDD-style, you are generally writing mocks (or fakes) so that your unit tests run at full speed.

As I said, a very interesting insight.