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:
- 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:
Post a Comment