Factory Pattern

In this blog post, I will try to create a simple program which will demonstrate the utility and significance of the factory pattern.
When:
  • when many classes are implementing an interface and you are unsure about which concrete implementation to return
  • when you want to separate creation logic from the object's main goal like when creating an object if you want to assign values to many properties and you want to separate this kinda logic from object's main functionality
  • if there are many if-else or switch statements for deciding which object to create
Why:
  • to encapsulate the creation of object
  • to separate the creation of object from the decision about which object to create
  • to allow adding new objects without breaking open closed principle (classes should be open for extension, closed for modification)
  • to allow (if needed) storing which objects to create in a separate place like database or configuration
How:
Let's create an example to see how factory pattern works. Let's say that we have a consumer which wants to perform search. The consumer doesn't care about which actual search engine does the search, as long as it can perform the search. From a factory perspective, we have multiple search providers and we want some kind of configuration to switch the providers. Let's take a look at the consumer code first.
class Program
{
        static void Main(string[] args)
        {
            ISearchEngineFactory factory = LoadFactory();

            ISearchEngine engine = factory.CreateMapSearchEngine();
            engine.Search();

            engine = factory.CreateNormalSearchEngine();
            engine.Search();

            Console.ReadLine();
        }

        private static ISearchEngineFactory LoadFactory()
        {
            string factoryName = Properties.Settings.Default.SearchFactory;
            return Assembly.GetExecutingAssembly().CreateInstance(factoryName) as ISearchEngineFactory;
        }
    }
In this code, first we create a factory by calling the LoadFactory() method. This method looks into the settings file to see which factory to load and then by using reflection creates an instance of that factory. The ISearchEngineFactory defines two methods as shown below: one for normal search and the other for map search.
public interface ISearchEngineFactory
{
        ISearchEngine CreateNormalSearchEngine();

        ISearchEngine CreateMapSearchEngine();
    }
In this example, we are defining two search engines: Google and Bing. So, first up, we create 2 factories for them as shown below:
public class GoogleFactory : ISearchEngineFactory
{
        public ISearchEngine CreateNormalSearchEngine()
        {
            return new Google() as ISearchEngine;
        }

        public ISearchEngine CreateMapSearchEngine()
        {
            Google g = new Google {Context = "Maps"};
            return g as ISearchEngine;
        }
    }
public class BingFactory : ISearchEngineFactory
{
        public ISearchEngine CreateNormalSearchEngine()
        {
            return new Bing() as ISearchEngine;
        }

        public ISearchEngine CreateMapSearchEngine()
        {
            return new BingMaps() as ISearchEngine;
        }
    }
In this example, we are also demonstrating, how the actual object creation can be different. The Bing engine returns Bing object for normal searches and BingMaps object for map searches. The Google engine for normal search, just returns the Google object with context as null. For maps search, it returns the normal google object with Context set as "Maps". All of these engines, implement the ISearchEngine interface which has a Search method and a context property as shown below.
public interface ISearchEngine
{
        void Search();

        string Context { get; set; }
    }
public class Bing : ISearchEngine
{
        public void Search()
        {
            Console.WriteLine("You searched on Bing");
        }

        public string Context { get; set; }

    }
public class BingMaps : ISearchEngine
{
        public void Search()
        {
            Console.WriteLine("You searched on Bing Maps");
        }

        public string Context { get; set; }
    }
public class Google : ISearchEngine
{
        public void Search()
        {
            string str = "You searched on Google";

            if (string.IsNullOrEmpty(Context))
                Console.WriteLine(str);
            else
                Console.WriteLine(str + " " + Context);
        }

        public string Context { get; set; }
    }
When you run this app, with search provider set as Google, you see the following output.



And if you go to settings page and change the settings configuration to use Bing as the search provider, the output is as below:




Once we have set up the factory pattern in our code, we can easily add newer factories and newer search engine classes without affecting any of the other classes, hence respecting the Open-Closed principle. Once that is done, you can easily change the configuration to point to say Yahoo factory and the factory pattern will take care of correct object creations.

No comments: