Refactoring: extract and override factory method
I’m sure you have seen classes that initialize a lot of objects in their constructor. These classes may be hard to test because of those object creations I mentioned. To get around this problem we use Extract and override factory method refactoring so we can extend these classes and override some factory methods.
Let’s see the following code.
public class XmlPaymentManager
{
private PaymentTranformer _transformer;
public XmlPaymentManager()
{
var readerPath = ConfigurationSettings.AppSettings["ReaderPath"];
var writerPath = ConfigurationSettings.AppSettings["WriterPath"];
var reader = new StreamReader(readerPath);
var writer = new StreamWriter(writerPath);
_transformer = new PaymentTranformer(reader, writer);
}
// ...
}
We can see here initialization of StreamReader and StreamWriter. We cannot use these classes for unit testing purposes because we don’t want to read and write files. If we do then we have integration tests as we may face file system issues. As a first step let’s move creation of PaymentTransformer class to separate method.
public class XmlPaymentManager
{
private PaymentTranformer _transformer;
public XmlPaymentManager()
{
_transformer = GetTransformer();
}
protected PaymentTranformer GetTransformer()
{
var readerPath = ConfigurationSettings.AppSettings["ReaderPath"];
var writerPath = ConfigurationSettings.AppSettings["WriterPath"];
var reader = new StreamReader(readerPath);
var writer = new StreamWriter(writerPath);
return new PaymentTranformer(reader, writer);
}
// ...
}
Now we can create extended class that overrider GetTransformer() method. For testing purposes we use FakePaymentTransformer.
public class TestXmlPaymentManager : XmlPaymentManager
{
protected PaymentTranformer GetTransformer()
{
return new FakeXmlPaymentTransformer();
}
}
Extract and override factory method helps you if you can extend the class you want to test. You may save a lot of time when you find larger hard to test classes and you can make them testable using this refactoring method.
It doesn’t work like that. You have to make GetTransformer virtual in order to be able to call it. That works in java, where all the functions are virtual, but not in c#. So, it should look like this:
class BaseClass
{
private string a;
public BaseClass()
{
a = CreateMethod();
}
protected virtual string CreateMethod()
{
return “aaa”;
}
}
class DerivedClass : BaseClass
{
protected override string CreateMethod()
{
return “bbb”;
}
}
I think the same thing can be achieved by using poor mans dependency injection.