April 17, 2009

Using a Factory to Generate Multiple Instances of a Dependency

Passing dependencies into a class or method leads to a cleaner, more testable design, but what if you need to create multiple instances of the same dependency? Consider the following, where the ImportHeadersMatchTemplateHeaders method needs to create two instances of the CsvReader class:

public class ImportFileValidator 
{
private readonly string _importFileName;
private readonly string _templateFileName;

public ImportFileValidator(string importFileName, string templateFileName)
{
_importFileName = importFileName;
_templateFileName = templateFileName;
}

public bool ImportHeadersMatchTemplateHeaders()
{
string[] importHeaders;
string[] templateHeaders;

using (IReader reader = new CsvReader(_importFileName))
{
importHeaders = reader.GetHeaders();
}

using (IReader reader = new CsvReader(_templateFileName))
{
templateHeaders = reader.GetHeaders();
}

return (importHeaders.Length == templateHeaders.Length)
}

// other validation methods
}

We could modify the constructor (or the ImportHeadersMatchTemplateHeaders method) to include two IReader parameters, but let’s say that in this case the method will always use the same type of IReader object both times.  To handle this we can have the ImportFileValidator class’s constructor also receive an object that creates the appropriate IReader objects:

public interface IReaderCreator 
{
IReader CreateReader(string fileName);
}

public class CsvReaderCreator : IReaderCreator
{
public IReader CreateReader(string fileName)
{
return new CsvReader(string fileName);
}
}

public class ImportFileValidator
{
private readonly string _importFileName;
private readonly string _templateFileName;
private readonly IReaderCreator _readerCreator;

public ImportFileValidator(string importFileName, string templateFileName, IReaderCreator readerCreator)
{
_importFileName = importFileName;
_templateFileName = templateFileName;
_readerCreator = readerCreator;
}

public bool ImportHeadersMatchTemplateHeaders()
{
string[] importHeaders;
string[] templateHeaders;

using (IReader reader = _readerCreator.CreateReader(_importFileName))
{
importHeaders = reader.GetHeaders();
}

using (IReader reader = _readerCreator.CreateReader(_templateFileName))
{
templateHeaders = reader.GetHeaders();
}

return (importHeaders.Length == templateHeaders.Length)
}

// other validation methods
}

This may seem like extra work and unnecessary complexity, but it’s really not.  It allows you to mock a class’s dependencies, making it easier and faster to test – and hearing the whoosh of your unit tests finishing lickity-split definitely makes it a satisfying choice.

0 comments:

Post a Comment