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.