Sunday, 10 June 2012

Factory Pattern

In this post I’ll be looking at the factory pattern. I’ll start off by looking at how the factory pattern is commonly used and then I’ll take a look at the strict GoF (Gang of Four) definition which is a little different and to be honest not as well known. The factory pattern is all about encapsulating object creation logic into a single object that can be used by other parts of your code.

I’ll start off with an example that describes a scenario where the factory pattern could be put to work. Imagine we have an application that takes data from multiple sources and converts that data into a common data standard. We’d probably use a data adapter of some sorts to convert the incoming data to our common data standard. To begin with were going to receive data in CSV format so our code for creating a data adapter object might look something like this.
DataAdapter csvAdapter = new CsvDataAdapter();
Imagine requirements change and we’re told that we also have to handle XML data from another source. So we set to work building an XML data adapter and then create that object as follows.
DataAdapter xmlAdapter = new XMLDataAdapter();
Because we’ve got two different types of data adapter we’ll need some sort of conditional logic in our application that decides which data adapter to use. This logic might look something like the following.
// default to XML data adapter
if (adapterType_i.equals("csv"))
{
     return new CsvDataAdapter();
}
else
{
     return new XmlDataAdapter();
}
It’s likely that we’ll have to decide which data adapter we need at a number of different points in our application.  Rather than duplicate this logic every time we need to create an adapter we’ll put this code into a method that it can be reused. It’s likely that in the future our application will need to support new data formats so this logic will need to be extended. To ensure our code is easier to maintain we need to encapsulate the parts that are likely to change the most. In this case that’s the object creation code so using the factory pattern to encapsulate this code away from the rest of our logic makes sense.

Creating the Factory
We’ll start by creating the actual factory class itself - the factory will simply encapsulate the logic we’ve already defined.
public class DataAdapterFactory
{
    protected String adapterType_i;

    public DataAdapterFactory(String adapterType_p)
    {
        adapterType_i = adapterType_p;
    }

    public DataAdapter createDataAdapter()
    {
        // default to XML data adapter
        if (adapterType_i.equals("csv"))
        {
             return new CsvDataAdapter();
        }
        else
        {
             return new XmlDataAdapter();
        }
    }
}
The factory objects constructor takes a single adapterType parameter which it uses to decide what type of DataAdapter to create and return to the client. You’ll notice that the return type of the createDataAdapter method is DataAdapter, even though the method can return either a CsvDataAdapter or an XmlDataAdapter object. The code is exhibits polymorphic behaviour as it allows calling code to use the adapters in the same way regardless of the actual implementation. For example, if a client is returned an XmlDataAdapter, it should be able to call the same functions on that object as if it were any other type of adapter.

In our case we’ll implement this polymorphic behaviour by ensuring that all objects returned by the factory extend our abstract DataAdapter class. Extending classes will be forced to implement the convertData method and by doing so provide a specific DataAdapter implementation.
public abstract class DataAdapter
{
    public DataAdapter()
    {

    }

    /* method template that extending classes are forced to 
       implement */
    public abstract String convertData(String inputData_p);
}

Creating the concrete adapter classes 
So what do the concrete adapter implementations look like? We’ll start off by defining the CsvDataAdapter as follows.
public class CsvDataAdapter extends DataAdapter
{
     public CsvDataAdapter()
     {

     }

     public String convertData(String inputData_p)
     {
          // some data adapter code could go here to convert CSV to common data standard
         return "csv data[" + inputData_p + "] " +  "converted to common data standard";
     }
}
This class extends abstract DataAdapter and overrides the mandatory convertData method. I’ll not be providing the csv transformation code but the class above should be sufficient to show you how a DataAdapter implementation class is structured.
public class XmlDataAdapter extends DataAdapter
{
    public XmlDataAdapter()
    {

    }

    public String convertData(String inputData_p)
    {
        // some data adapter code could go here to convert XML to common data standard
        return "XML data[" + inputData_p + "] " +  "converted to common data standard";
    }
}
The relationship between the abstract DataAdapter and its subclasses is shown below.


Testing our factory
Before we test our factory here is a summary of what we’ve created so far.
  • DataAdaptrerFactory class 
  • DataAdapter abstract class 
  • CsvDataAdapter concrete implementation class 
  • XmlDataAdapter concrete implementation class
Now let’s see the factory pattern in action with a simple test harness.
public class TestFactoryPattern
{
    public static void main (String[] args_p)
    {

        DataAdapterFactory adapterFactory;
        DataAdapter dataAdapter;
        String convertedData;

        adapterFactory = new DataAdapterFactory("csv");
        dataAdapter = adapterFactory.createDataAdapter();
        convertedData = dataAdapter.convertData("some, simple, csv, data");

        System.out.println(convertedData);

        adapterFactory = new DataAdapterFactory("xml");
        dataAdapter = adapterFactory.createDataAdapter();
        convertedData = dataAdapter.convertData("<TestData>data</TestData>");

        System.out.println(convertedData);
    }
}
On line 10 we create a new factory object passing in the adapter type. On line 11 we then use the factory’s createDataAdapter method to get a reference to a new CsvDataAdapter object. Note that this object is assigned to a DataAdapter variable - this is possible because CsvDataAdapter is derived from the base type DataAdapter. On line 12 we invoke the convertData method and see it print some csv specific output. Lines 16, 17 and 18 follow a similar pattern with a new factory object being used to create an XmlDataAdapter. Once again this object is assigned to its parent type (DataAdapter) and when we invoke its convertData method we’ll see some XML specific output.

This simple example shows how we can use a factory class to create a variety of different object types. We’ve encapsulated our object creation logic inside a dedicated class making it easier to maintain than if it were scattered throughout our (hypothetical) application.

GoF factory pattern
The pattern I described above is a recognised and commonly used form of the factory pattern. However the formal GoF definition of the factory pattern is slightly different and states.
‘The factory pattern should define an interface for creating an object, but let subclasses decide which classes to instantiate. Factory method lets a class defer instantiation to subclasses’.
 The GoF definition of the factory class allows greater flexibility by allowing you to define how factory methods should be structured but leave it up to the sub classer to implement the actual factory.

Creating an abstract factory
In order to allow subclasses to decide which class to instantiate we’ll need to define the factory as an abstract class. The abstract class is a specification for a factory and the actual implementation is the responsibility of the developer that extends it.

Our factory specification is defined with the abstract class below.
public abstract class AbstractDataAdapterFactory
{
    public AbstractDataAdapterFactory()
    {

    }

    /* our abstract method that subclassers will implement */
    public abstract DataAdapter createDataAdapter(String adapterType_p);
}

Creating a concrete factory
Now that we have our specification we need to create a factory implementation by extending the abstract class. Our factory implementation will look similar to the factory created earlier except that it will extend the specification defined by our abstract class. You’ll notice that the factory returns the same types that were returned by our earlier example. We could have created new DataAdapter implementations but for convenience we’ll stick with the ones we defined in our earlier example (CsvDataAdapter, XlDataAdapter).

Testing our GoF factory
Now let’s see the GoF factory pattern in action with a simple test harness. You’ll notice that this test harness is almost identical to the one used earlier, except that this time we create an instance of our DataAdapterFactoryImpl class that extends the AbstractDataAdapterFactory.
public class TestGoFFactoryPattern
{
    public static void main (String[] args_p)
    {

        AbstractDataAdapterFactory adapterFactory;
        DataAdapter dataAdapter;
        String convertedData;

        adapterFactory = new DataAdapterFactoryImpl();
        dataAdapter = adapterFactory.createDataAdapter("csv");
        convertedData = dataAdapter.convertData("some, simple, csv, data");

        System.out.println(convertedData);

        adapterFactory = new DataAdapterFactoryImpl();
        dataAdapter = adapterFactory.createDataAdapter("xml");
        convertedData = dataAdapter.convertData("<TestData>data</TestData>");

        System.out.println(convertedData);
    }
}
On line 10 we create a new factory implementation and assign it to an AbstractDataAdapterFactory reference. This differs from the first example because we can now potentially have a number of different types of factory, each of which could be assigned to a AbstractDataAdapterFactory reference. The rest of this test class is almost identical to the test class used in the first example.

Why use the GoF factory pattern?
At this point you may well be asking why would I use the GoF factory pattern instead of the more traditional version described in the first part of this post? In both examples we have a factory object that encapsulates object creation logic and return objects that extend a base type. Rather than have a single factory class instantiate all our objects (like our first example), the GoF version allows us to have one or more sub classes that handle object creation. This allows greater flexibility because the abstract factory can be easily extended to support new requirements rather than continually having to amend a single factory class.  The GoF factory pattern is worth considering when there are a number of developers extending your factory class and making substantial changes to the base implementation. In these circumstances you would be better off defining a specification for the factory (abstract class) and letting the developers who are overriding it take care of the implementation.

No comments:

Post a Comment