Saturday, October 10, 2009

Factory Pattern

The factory pattern is probably of the most common creational pattern.

What is a "creational" pattern?

A creational pattern is pattern which describes ways to create objects. You can call it an abstraction of the new operator. The gain of factory classes is to encapsulate the creation of objects to a central position - the factory - and ensure that an object becomes correct initialized. A well known factory is the static XmlWriter.Create() method. It gets either a file name or an output stream and an optional XmlWriterSettings parameter which specifies things like formatting behavior of the returned XmlWriter.

Sample Scenario
Before we start to show how the factory works I'll define a sample scenario. Our system is a ordering system which handles "Order" and "Customer" objects and we got the following classes.
public class Order : ICreationInformation
{
   public DateTime CreationDate { get; set; }
   public String CreationUser { get; set; }
   public String OrderNo { get; set; }
   public Decimal Amount { get; set; }
   public Customer Customer { get; set; }
}

public class Customer : ICreationInformation
{
   public DateTime CreationDate { get; set; }
   public String CreationUser { get; set; }
   public String Name { get; set; }

   public override string ToString()
   {
      return this.Name;
   }
}
As you can see the "Order" as well as the "Customer" contain the system fields "CreationDate" and "CreationUser". In addition the order only becomes valid if it has a related "Customer".

Problem of using the "new" operator
Our ordering system might have more than one position where orders become created. If you work with new operators to create a new "Order" wherever it is possible to create a new order you always have to specify the "CreationDate" and the "CreationUser". As first you might forget to set this information at one point. Remember Murphys law "Anything that can go wrong will go wrong.". As second you duplicate your source code. If you start with a "CreationDate" from local system it might be a future requirement to change this to use a server date since many computers don't have a correct configured local time. In this case you have to fly all over your code to find all position where you worked with local system time to set a "CreationDate".

Factory
A factory class is a class which encapsulates the creation and correct initialization of objects.

The following listing shows a sample factory to create "Customer" and "Order" objects.
public class Factory
{
   public Customer CreateCustomer()
   {
      Customer customer = new Customer();

      customer.CreationDate = DateTime.Now;
      customer.CreationUser =
         System.Security.Principal.WindowsIdentity.GetCurrent().Name;

      return customer;
   }

   public Order CreateOrder()
   {
      Order order = new Order();

      order.CreationDate = DateTime.Now;
      order.CreationUser =
         System.Security.Principal.WindowsIdentity.GetCurrent().Name;

      return order;
   }
}
Once created we can use our factory wherever we need a new "Order" or "Customer" object.
Factory factory = new Factory();

Customer customer = factory.CreateCustomer();
customer.Name = "ALFKI";

Order order = factory.CreateOrder();
order.Customer = customer;
To ensure that your objects are not created by using their constructor from outside the factory it makes sense to define an internal constructor - if your objects are stored in a separate assembly. Now it is impossible to create one of these business objects without a correct initialization.

In our simple example one factory fits to create both types of objects. In a real world scenario it might make sense to use specific factory classes for each type of a business object.

Abstract Factory
An abstract factory is a factory which can create different types of objects with a single implementation. In .NET a abstract factory can be realized with a generic method (or class).

Maybe you noticed the ICreationInformation interface which was added to our "Order" and "Customer" classes. Here is the definition of our this interface which provides the properties "CreationDate" and "CreationUser".
interface ICreationInformation
{
   DateTime CreationDate { get; set; }
   String CreationUser { get; set; }
}
In addition we don't specify any constructor, so both objects have an implicit public constructor. This enables us to create a generic function which can handles the creation of both objects with one implementation.
public class AbstractFactory
{
   public T Create<T>()
      where T : ICreationInformation, new()
   {
      T instance = new T();

      instance.CreationDate = DateTime.Now;
      instance.CreationUser =
         System.Security.Principal.WindowsIdentity.GetCurrent().Name;

      return instance;
   }
}
As you see, the method "Create" works with a generic type "T" with two constraints. The specified type has to be an implementation of ICreationInformation and it needs an empty public constructor. If your business objects don't support an empty constructor you can use the not generic static method System.Activator.Create() which creates objects and enables you to specify constructor arguments.

The usage of this factory is quiet equal to the usual factory, you just have only one method and have to define the generic type.
AbstractFactory factory = new AbstractFactory();

Customer customer = factory.Create<Customer>();
customer.Name = "ALFKI";

Order order = factory.Create<Order>();
order.Customer = customer;
Now that's cool. First, because we have just one implementation for probably hundreds of different objects which implement our ICreationInformation interface and provide an empty public constructor. Second, if you a little bit like me, you are still feeling like a kid sometimes and have to say these generic approaches are always cool! :-)

I'm afraid to say, these abstract factories are not as cool as they look at the first moment. The generic approach is a huge restriction for a factory. Suggest you would be more restrictive as we have been since now. Eventually you wouldn't like to enable the user of your factory to create any "Order" without specifying a "Customer". In a usual factory you just change the "CreateOrder" function to get an instance of a "Customer" object. An abstract factory cannot handle special business cases like this.

Combine Both
As we saw an abstract factory seems to be nice to do some general initializations for objects, but it's a week solution for specific requirements. Anyway, it helps to avoid duplicated code. So why not combining both? You should use a non abstract factory to create new business objects within an application which consumes your business layer but you can use the abstract factory within other factories.

Here is a new version of our factory classes.
class AbstractFactory
{
   internal T Create<T>()
      where T : ICreationInformation, new()
   {
      T instance = new T();

      instance.CreationDate = DateTime.Now;
      instance.CreationUser =
         System.Security.Principal.WindowsIdentity.GetCurrent().Name;

      return instance;
   }
}

public class Factory
{
   AbstractFactory _abstract = new AbstractFactory();

   public Customer CreateCustomer()
   {
      Customer customer = _abstract.Create<Customer>();
      return customer;
   }

   public Order CreateOrder(Customer customer)
   {
      if (customer == null)
         throw new ArgumentNullException("customer");

      Order order = _abstract.Create<Order>();
      order.Customer = customer;

      return order;
   }
}
As you see, the abstract factory became a private class. So it is not possible to use it from outside of our assembly. The usual factory has an instance of an abstract factory to use it's gain to avoid duplicate code for the ICreationInformation interface members. The "CreateOrder" method became more restrictive to ensure that no order can be created without a specified instance of a "Customer".

Now the consumer can use a secure and handy factory and everything is ensured to be done correct.
Factory factory = new Factory();

Customer customer = factory.CreateCustomer();
customer.Name = "ALFKI";

Order order = factory.CreateOrder(customer);

Conclusion
As you saw, the factory pattern is a helpful way to ensure correct initialized objects. The abstract factory is more a tool to be used for less complex requirements but it can be very helpful to do basic initializations.

No comments:

Post a Comment