Generic Factory Pattern, Open/Closed

The Factory Pattern is a common software development code pattern for creating objects. In this Factory Pattern example we will create a Generic Factory that conforms to the open/closed principle.

open/closed principle
Software classes, modules, functions etc. should be open for extension, but closed for modification

So in this example we will create a Factory that can be extended without having to edit the Factory class.

The quasi-real world example used here is based on returning Workflow Controllers for managing content of different types within a Content Management System.

First lets create the Interface that the objects the Factory returns will all conform to:


namespace AdvancedGenericFactoryPatternTestApp
{
    public interface IWorkflowController
    {
        void Approve();
        void Update();
        void Reject();
        void Archive();
    }
}

Now lets create the two example Workflow Controllers.

News content WorkflowController.


namespace AdvancedGenericFactoryPatternTestApp.Controllers
{
    public class NewsWorkflowController : IWorkflowController
    {
        private readonly bool _enableAuditing;

        public NewsWorkflowController()
        {
            Console.WriteLine("Auditing: " + _enableAuditing);
        }

        public NewsWorkflowController(bool enableAuditing)
        {
            _enableAuditing = enableAuditing;
            Console.WriteLine("Auditing: " + _enableAuditing);
        }

        public void Approve()
        {
            //logic here
            Console.WriteLine("NewsWorkflowController.Approve");
        }

        public void Update()
        {
            //logic here
            Console.WriteLine("NewsWorkflowController.Update");
        }

        public void Reject()
        {
            //logic here
            Console.WriteLine("NewsWorkflowController.Reject");
        }

        public void Archive()
        {
            //logic here
            Console.WriteLine("NewsWorkflowController.Archive");
        }
    }
}

Events content WorkflowController.


namespace AdvancedGenericFactoryPatternTestApp.Controllers
{
    public class EventsWorkflowController : IWorkflowController
    {
        private readonly bool _enableAuditing;

        public EventsWorkflowController()
        {
            Console.WriteLine("Auditing: " + _enableAuditing);
        }

        public EventsWorkflowController(bool enableAuditing)
        {
            _enableAuditing = enableAuditing;
            Console.WriteLine("Auditing: " + _enableAuditing);
        }

        public void Approve()
        {
            //logic here
            Console.WriteLine("EventsWorkflowController.Approve");
        }

        public void Update()
        {
            //logic here
            Console.WriteLine("EventsWorkflowController.Update");
        }

        public void Reject()
        {
            //logic here
            Console.WriteLine("EventsWorkflowController.Reject");
        }

        public void Archive()
        {
            //logic here
            Console.WriteLine("EventsWorkflowController.Archive");
        }
    }
}

Notice that the Workflow Controllers has two Constructors, one of which takes a Boolean value.

OK, now we are ready to create our Generic Workflow Controller Factory


namespace AdvancedGenericFactoryPatternTestApp
{
    public class Factory< T >
    {
        private readonly Dictionary< string, Type > _factoryDictionary = new Dictionary< string, Type >();

        public Factory()
        {
            Console.WriteLine("Factory Constructor");

            Type[] types = Assembly.GetAssembly(typeof (T)).GetTypes();

            foreach (Type type in types)
            {
                if (!typeof (T).IsAssignableFrom(type) || type == typeof (T))
                {
                    // Incorrect type
                    continue;
                }

                Console.WriteLine(string.Format("Factory adding: {0}", type.Name));

                // Add the type
                _factoryDictionary.Add(type.Name, type);
            }
        }

        public T Create< V >(params object[] args)
        {
            return (T) Activator.CreateInstance(_factoryDictionary[typeof (V).Name], args);
        }
    }
}

The Constructor uses Reflection on current assembly to find all the types based on Type T and then stores them into a Dictionary. Type T in our case is the Interface.

The Create method takes a Type V and an array of arguments of type Object, and returns an Object of Type T. Which will be the Interface.

Next lets use the Generic Factory to manage some content.


namespace AdvancedGenericFactoryPatternTestApp
{
    internal class Program
    {
        private static IWorkflowController _controller;

        private static void Main(string[] args)
        {
            Factory< IWorkflowController > factory = new Factory< IWorkflowController >();

            object[] controllerArguments = {true};

            _controller = factory.Create< EventsWorkflowController >(controllerArguments);

            _controller.Approve();
            _controller.Update();
            _controller.Reject();
            _controller.Archive();


            _controller = factory.Create< NewsWorkflowController >();

            _controller.Approve();
            _controller.Update();
            _controller.Reject();
            _controller.Archive();

            Console.ReadKey();
        }
    }
}

Notice that in the case of the Events Workflow Controller we pass in an Object Array with a Boolean value of true.

This is the output


Factory Constructor
Factory adding: NewsWorkflowController
Factory adding: EventsWorkflowController
Auditing: True
EventsWorkflowController.Approve
EventsWorkflowController.Update
EventsWorkflowController.Reject
EventsWorkflowController.Archive
Auditing: False
NewsWorkflowController.Approve
NewsWorkflowController.Update
NewsWorkflowController.Reject
NewsWorkflowController.Archive

You can see from the output that the Object Array value was passed to the correct Constructor.

You can download the Advanced Generic Factory Pattern here



Comments

No comments yet.

Add Yours

  • Author Avatar

    YOU


Comment Arrow




About Author

Robert

Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning hands down.