Tuesday, October 13, 2009

Design Patterns – Abstract Factory Pattern

Factories are objects that encapsulate the logic for creating other objects.

Factory object could create one or another object based on some configuration parameters. Or the factory could decide what kind of concrete object to create based on a parameter to some object creational method. For example if the method received post-code the factory creates City object and when the parameter is an email address the factory creates Customer object. In that way one might create factory to select one or another kind of object from database based on the user input.

Abstract Factory



The Abstract Factory is that encapsulates the way of creating concrete objects that have lot of common.

Often the created from abstract factory objects derive from same base class or share some interface. Abstract factory object for example could be used to create fake objects for the purpose of testing the software, and create regular objects otherwise in production mode. The fake object and the regular object derive from same base class or share same interface. The client code deals only with instances of the base type and has no knowledge of the concrete implementation.

I will give an example of abstract factory created with C#.

Reflection based abstract factory



The central feature of our abstract factory is to create concrete objects based on key/value pairs.

We register the types the factory is able to create into a dictionary. For registration of the types the factory may create we expose method Register().

Our base abstract factory class uses reflection to call the object constructors of the concrete type. That is done in method CreateInstance(). We invoke CreateInstance() with:



  • key - that will be used to create one concrete object or another as it is registered already via Register().


  • typeParams - if the concrete object has parameterized constructor which we want to use, then we specify the parameter types in an array.


  • valParams - if the concrete object has parameterized constructor which we want to use, then we specify the parameter values in an array.





Following is a implementation of reflection based abstract factory:




// T1 is the appropriate key type.
// T2 is the base type from which derive all the types the factory can create.
public abstract class AbstractFactory<T1, T2>
{
// Creates the factory.
// T1 is the appropriate type.
// T2 is the base type from which derive all the types the factory can create.
public static AbstractFactory<T1, T2> CreateFactory()
{
throw new Exception("Override FactoryReflected.CreateFactory()!");
}

// Register for types the factory can create.
protected Dictionary<T1, Type> _registered = new Dictionary<T1, Type>();

// Registers the specified factory types.
protected void Register(T1 key, Type type)
{
_registered.Add(key, type);
}


// Creates new instance of type by enumeration key.
public T2 CreateInstance(T1 key, Type[] typeParams, object[] valParams)
{
Type type = _registered[key];

System.Reflection.ConstructorInfo cInfo =
type.GetConstructor(typeParams);

return (T2)cInfo.Invoke(valParams);
}

}


We also have used generics to make the implementation of reflection based abstract factory more generalized.




Personal name



Let's face the problem and how we are going to find a solution. The different societies have different conventions for personal full name. In Russia the common order is "family-name given-name". In some situations the family-name is capitalized. In west Europe the usual convention is "given-name family-name", etc.

Our problem is - we are going to use the appropriate form of address when we communicate with people from different regions. For simplicity assume we have the family and the given name. We need a way to compose appropriate full name.

First we have the base class Name. The Name class has first and last name properties. We also have an abstract method GetFullName() which should give different result for different society. Furthermore, the concrete implementation will be done in derived classes:



// Base abstract class from which concrete implementations derive
public abstract class Name
{ // see http://en.wikipedia.org/wiki/Full_name#Naming_convention

protected string firstName;
protected string lastName;
protected Name() { }
public Name(string first, string last)
{
this.firstName = first;
this.lastName = last;
}

// Full name can be composed differently
// as the different societies have they different
// naming conventions
public abstract string GetFullName();
}

// composing Western full name
public class WesternName : Name
{
public WesternName(string first, string last) : base(first, last) { }
public override string GetFullName() { return firstName + " " + lastName; }
}

// composing Eastern full name
public class EasternName : Name
{
public EasternName(string first, string last) : base(first, last) { }
public override string GetFullName() { return lastName + " " + firstName; }
}

// another Eastern full name convention
public class EasternOfficialName : Name
{
public EasternOfficialName(string first, string last) : base(first, last) { }
public override string GetFullName() { return lastName.ToUpper() + " " + firstName; }
}





Concrete factory



Having the abstract factory from above we create concrete factory used for creation of objects of classes derived from the Name class. The NameFactory contains the initialization of itself in method CreateFactory(). If we create new concrete class derived from class Name to fit another convention for personal full name then we also have to register that new class in CreateFactory() method.



public class NameFactory : AbstractFactory<string, Name>
{
private NameFactory() { }

public static new NameFactory CreateFactory()
{
NameFactory factory = new NameFactory();

// Register which concrete type to create for which key
factory.Register("WesternOrder", typeof(WesternName));
factory.Register("EasternOrder", typeof(EasternName));
factory.Register("EasternOfficialOrder", typeof(EasternOfficialName));

return factory;
}
}




We will create instances of NameFactory only via the static method CreateFactory() and thereof we have specified the default constructor to be private.






Using the concrete factory



We create nameFactory object and then call one after another GetFullName() of all possible derived from class Name objects. These derived from Name objects we create as we call method CreateInstance() of nameFactory.



class Program
{
static void Main(string[] args)
{
NameFactory nameFactory = NameFactory.CreateFactory();

foreach (string convention in new string[] {
"WesternOrder", "EasternOrder", "EasternOfficialOrder", })
{
Name name = nameFactory.CreateInstance(convention,
new Type[] { typeof(string), typeof(string) },
new object[] { "Atanas", "Hristov" });

Console.WriteLine(name.GetFullName());
}


Console.Read();
}
}



And finally the output to the console:






Atanas Hristov

kick it on DotNetKicks.com
Shout it

No comments:

Post a Comment