Abstract Factory Pattern

The abstract factory pattern is a software design pattern that provides a way to encapsulate a group of individual factories that have a common theme. In normal usage, the client software creates a concrete implementation of the abstract factory and then uses the generic interfaces to create the concrete objects that are part of the theme. The client does not know (or care) which concrete objects it gets from each of these internal factories since it uses only the generic interfaces of their products. This pattern separates the details of implementation of a set of objects from their general usage.

The UML below represents how to implement the AbstractFactory pattern.

Abstract Factory Pattern

Let’s consider an example. We need a factory class that will return different brands of cars that include Ford, Chevrolet, and Dodge. Let’s first start by creating an enumeration for the different brands our factory will support.

    public enum AutomobileBrands
    {
        Ford,
        Chevrolet,
        Dodge
    }

Next, let’s define interfaces for our automobile factories as well as the automobiles themselves. We could add more methods for the auto’s, but for now, only two methods for “Start” and “Stop” will be part of the interface.

    public interface IAutomobileFactory
    {
        Automobile CreateAutomobile();
    }

    public interface IAutomobile
    {
        void Start();

        void Stop();
    }

The factory will return an abstract class that defines the common methods and properties for all automobiles. Let’s define this next. The class will have the ToString method overriden so we can display the brand of the automobile as output later on.

    public abstract class Automobile : IAutomobile
    {
        protected AutomobileBrands BrandName { get; set; }

        public virtual void Start()
        {
            Console.WriteLine(String.Format("The {0} has started.", BrandName));
        }

        public virtual void Stop()
        {
            Console.WriteLine(String.Format("The {0} has stopped.", BrandName));
        }

        public override string ToString()
        {
            return String.Format("This is a {0} automobile.", BrandName);
        }
    }

The BrandName property is defined as protected since it will only be defined in the concrete classes. Now let’s define three concrete classes for each of the brands our factory will support creating. Each will implement the IAutomobile interface defined above. I’ve overridden the Start method on a the Chevrolet and Dodge classes to show specific behavior of those brands of automobiles. There’s a lot more differences, but for now let’s keep it simple by showing only a difference with how they start.

    class Ford : Automobile
    {
        public Ford()
        {
            BrandName = AutomobileBrands.Ford;
        }
    }

    class Chevrolet : Automobile
    {
        public Chevrolet()
        {
            BrandName = AutomobileBrands.Chevrolet;
        }

        public override void Start()
        {
            Console.WriteLine(String.Format("The {0} starts Fast.", BrandName));
        }
    }

    class Dodge : Automobile
    {
        public Dodge()
        {
            BrandName = AutomobileBrands.Dodge;
        }

        public override void Start()
        {
            Console.WriteLine(String.Format("The {0} is slowly starting up.", BrandName));
        }
    }

Now let’s create the factories for each brand. Each factory must implement the IAutomobileFactory interface so the main factory class (see below) can call the CreateAutomobile method. Each factory is marked as internal since we only want the main factory class to interact with these classes.

    internal class FordFactory : IAutomobileFactory
    {
        public  Automobile CreateAutomobile()
        {
            return new Ford();
        }
    }

    internal class ChevroletFactory : IAutomobileFactory
    {
        public Automobile CreateAutomobile()
        {
            return new Chevrolet();
        }
    }

    internal class DodgeFactory : IAutomobileFactory
    {
        public Automobile CreateAutomobile()
        {
            return new Dodge();
        }
    }

Now that we’ve defined a factory for each brand, let’s create a static factory class that will be used for creating all types of automobiles. This class will act as the facade for all automobile factories.

    public static class AutomobileFactory
    {
        public static Automobile CreateAutomobileByBrand(AutomobileBrands brand)
        {
            IAutomobileFactory factory;
            switch (brand)
            {
                case AutomobileBrands.Ford:
                    factory = new FordFactory();
                    break;
                case AutomobileBrands.Chevrolet:
                    factory = new ChevroletFactory();
                    break;
                case AutomobileBrands.Dodge:
                    factory = new DodgeFactory();
                    break;
                default:
                    throw new ArgumentOutOfRangeException(brand.ToString(), brand, "Brand is not a valid automobile brand.");
            }

            return factory.CreateAutomobile();
        }
    }

The factory class above only requires one argument which is used to determine what kind of automobile class to create. Now let’s put all this to test by writing some test code in the Main method of the application.

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Ford));
            Console.WriteLine(AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Chevrolet));
            Console.WriteLine(AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Dodge));

            IAutomobile ford = AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Ford);
            ford.Start();
            ford.Stop();

            IAutomobile chevy = AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Chevrolet);
            chevy.Start();
            chevy.Stop();

            IAutomobile dodge = AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Dodge);
            dodge.Start();
            dodge.Stop();

            Console.ReadLine();
        }
    }

When the program runs, the output displayed in the console is:

This is a Ford automobile.
This is a Chevrolet automobile.
This is a Dodge automobile.
The Ford has started.
The Ford has stopped.
The Chevrolet starts fast.
The Chevrolet has stopped.
The Dodge is slowly starting up.
The Dodge has stopped.

Here’s all of it put together:


using System;

namespace AbstractFactoryExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Ford));
            Console.WriteLine(AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Chevrolet));
            Console.WriteLine(AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Dodge));

            IAutomobile ford = AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Ford);
            ford.Start();
            ford.Stop();

            IAutomobile chevy = AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Chevrolet);
            chevy.Start();
            chevy.Stop();

            IAutomobile dodge = AutomobileFactory.CreateAutomobileByBrand(AutomobileBrands.Dodge);
            dodge.Start();
            dodge.Stop();

            Console.ReadLine();
        }
    }

    public static class AutomobileFactory
    {
        public static Automobile CreateAutomobileByBrand(AutomobileBrands brand)
        {
            IAutomobileFactory factory;
            switch (brand)
            {
                case AutomobileBrands.Ford:
                    factory = new FordFactory();
                    break;
                case AutomobileBrands.Chevrolet:
                    factory = new ChevroletFactory();
                    break;
                case AutomobileBrands.Dodge:
                    factory = new DodgeFactory();
                    break;
                default:
                    throw new ArgumentOutOfRangeException(brand.ToString(), brand, "Brand is not a valid automobile brand.");
            }

            return factory.CreateAutomobile();
        }
    }

    #region Interfaces & Enum
    public enum AutomobileBrands
    {
        Ford,
        Chevrolet,
        Dodge
    }

    public interface IAutomobileFactory
    {
        Automobile CreateAutomobile();
    }

    public interface IAutomobile
    {
        void Start();

        void Stop();
    }
    #endregion

    public abstract class Automobile : IAutomobile
    {
        protected AutomobileBrands BrandName { get; set; }

        public virtual void Start()
        {
            Console.WriteLine(String.Format("The {0} has started.", BrandName));
        }

        public virtual void Stop()
        {
            Console.WriteLine(String.Format("The {0} has stopped.", BrandName));
        }

        public override string ToString()
        {
            return String.Format("This is a {0} automobile.", BrandName);
        }
    }

    #region Concrete Autos
    class Ford : Automobile
    {
        public Ford()
        {
            BrandName = AutomobileBrands.Ford;
        }
    }

    class Chevrolet : Automobile
    {
        public Chevrolet()
        {
            BrandName = AutomobileBrands.Chevrolet;
        }

        public override void Start()
        {
            Console.WriteLine(String.Format("The {0} starts Fast.", BrandName));
        }
    }

    class Dodge : Automobile
    {
        public Dodge()
        {
            BrandName = AutomobileBrands.Dodge;
        }

        public override void Start()
        {
            Console.WriteLine(String.Format("The {0} is slowly starting up.", BrandName));
        }
    }
    #endregion

    #region Concrete Auto Factories
    internal class FordFactory : IAutomobileFactory
    {
        public  Automobile CreateAutomobile()
        {
            return new Ford();
        }
    }

    internal class ChevroletFactory : IAutomobileFactory
    {
        public Automobile CreateAutomobile()
        {
            return new Chevrolet();
        }
    }

    internal class DodgeFactory : IAutomobileFactory
    {
        public Automobile CreateAutomobile()
        {
            return new Dodge();
        }
    }
    #endregion
}

Here’s some additional resources to find information about the Abstract Factory pattern.

http://en.wikipedia.org/wiki/Abstract_factory_pattern

http://www.developer.com/design/article.php/626001/Pattern-Summaries-Abstract-Factory-Pattern.htm

http://www.dofactory.com/Patterns/PatternAbstract.aspx

Advertisements