Sunday 19 December 2010

Abstract Factory Pattern

"Provide an interface for creating families of related or dependent objects without specifying their concrete classes."



When trying to get my head around Abstract Factory I think of it as being similar to the Bridge pattern, in that you have two levels of abstractions or two levels of things that can change.  If the client is using some class dispenser then it gets hold of an abstract factory which then creates objects and hands them back to the Client, also as abstractions.  There are two things that can change without the Client needing to be rebuilt:  Either the existing factory implementation can hand out objects which implement the existing Product interface, or the entire factory can be changed to return a new family of objects.
In the code example below we have two families of objects - DogToys and ChildToys. By using a common IBall interface we can change the Ball type that we create. But by further abstracting the factory through the use of an abstract factory we can create a new family of objects which also implement the same IBall interface.  Again this is all best visualised if we imagine that the objects are created through some class dispenser or factory.  In the code below the class responsible for creating our Factory instance is thus called the FactoryFactory!

using System;
 
namespace AbstractFactory
{
    class Program
    {
        /// <summary>
        /// Our client
        /// </summary>
        static void Main(string[] args)
        {
            // These are our only declarations - 
            // we can change our ball implementation 
            // or our factory implementation.
            ToyFactory factoryImpl;
            IBall ballImpl;
 
            factoryImpl =
            FactoryFactory.Create("AbstractFactory.DogToyFactory");
         
            ballImpl = factoryImpl.CreateBall(
            "AbstractFactory.TennisBall");
            ballImpl.Bounces();
         
            // Example 1:
            // We can change the "Product" ball type
            ballImpl = factoryImpl.CreateBall(
            "AbstractFactory.RubberBall");
            ballImpl.Bounces();
 
            //Example 2:
            // We can also change the "Factory". Now we will get a
            // product that belongs to the family of Products
            // associated with this Factory type.

            factoryImpl =
            FactoryFactory.Create
            ("AbstractFactory.ChildsToyFactory");
            ballImpl = factoryImpl.CreateBall(
            "AbstractFactory.Football");
            ballImpl.Bounces();
 
            Console.ReadLine();
        }     
    }
 
    /// <summary>
    /// Nothing to do with the Pattern itself, it's a factory 
    /// class that creates instances of our ToyFactory and is 
    /// used to decouple the client from any concrete implementations.
    /// </summary>
    public class FactoryFactory
    {
        public static ToyFactory Create(string factoryType)
        {
            return Activator.CreateInstance
            (Type.GetType(factoryType))
                as ToyFactory;
        }
    }
 
    // Pattern participants begins here...
 
    /// <summary>
    /// The abstract Factory
    /// </summary>
    public abstract class ToyFactory
    {
        public abstract IBall CreateBall(string ballType);
        public abstract ISqueakyToy CreateSqueakyToy(string toyType);
    }
 
    /// <summary>
    /// A ConcreteFactory
    /// </summary>
    public class DogToyFactory : ToyFactory
    {
        public override IBall CreateBall(string ballType)
        {
            return Activator.CreateInstance(Type.GetType(ballType))
                as IBall;
        }
 
        public override ISqueakyToy CreateSqueakyToy(string toyType)
        {
            return Activator.CreateInstance(Type.GetType(toyType))
                as ISqueakyToy;
        }
    }
 
    /// <summary>
    /// An abstract Product
    /// </summary>
    public interface IBall
    {
        void Bounces();
    }
 
    /// <summary>
    /// An concrete Product
    /// </summary>
    public class TennisBall : IBall
    {
        public void Bounces()
        {
            Console.WriteLine("{0} bounces"this.GetType().Name);
        }
    }
 
    /// <summary>
    /// Another concrete Product
    /// </summary>
    public class RubberBall : IBall
    {
        public void Bounces()
        {
            Console.WriteLine("{0} bounces"this.GetType().Name);
        }
    }
 
    /// <summary>
    /// An second abstract Product
    /// </summary>
    public interface ISqueakyToy
    {
        void Squeaks();
    }
 
    /// <summary>
    /// Another concrete Product
    /// </summary>
    public class PlasticChicken : ISqueakyToy
    {
        public void Squeaks()
        {
        }
    }
 
    /// <summary>
    /// Another concrete Product
    /// </summary>
    public class RubberBone : ISqueakyToy
    {
        public void Squeaks()
        {
        }
    }
 
    /// <summary>
    /// Another ConcreteFactory
    /// </summary>
    public class ChildsToyFactory : ToyFactory
    {
        public override IBall CreateBall(string ballType)
        {
            return Activator.CreateInstance(Type.GetType(ballType))
                as IBall;
        }
 
        public override ISqueakyToy CreateSqueakyToy(string toyType)
        {
            return Activator.CreateInstance(Type.GetType(toyType))
                as ISqueakyToy;
        }
    }
 
    /// <summary>
    /// Another concrete Product
    /// </summary>
    public class BathDuck : ISqueakyToy
    {
        public void Squeaks()
        {
        }
    }
 
    /// <summary>
    /// Another concrete Product
    /// </summary>
    public class WhoppeeCushion : ISqueakyToy
    {
        public void Squeaks()
        {
            Console.WriteLine(
            "{0} sort of squeaks!"this.GetType().Name);
        }
    }
 
    /// <summary>
    /// Another concrete Product
    /// </summary>
    public class Football : IBall
    {
        public void Bounces()
        {
            Console.WriteLine("{0} bounces"this.GetType().Name);
        }
    }
}
Produces:

TennisBall bounces
RubberBall bounces
Football bounces

No comments:

Post a Comment