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.
