
In class-based programming, the factory method pattern is a creational pattern which uses factory methods to deal with the problem of creating objects without specifying the exact class of object that will be created. This is done by creating objects via a factory method, which is either specified in an interface (abstract class) and implemented in implementing classes (concrete classes); or implemented in a base class (optionally as a template method), which can be overridden when inherited in derived classes; rather than by a constructor.
- When we want to encapsulate/hide the logic from clients who use it.
- When we are producing families of the same type.
- When a system should be independent of how its products are created, composed and represented.
- When we want to avoid series of if statements, switch and new keywords on the client.
- Maintainability, low coupling, and high cohesion.
In this example we will create an AnimalFactory that implements IAnimal interface. For the purpose of demonstration let us keep this simple. The interface has a method of Talk() that will just display how the animal talks on the console.
The Source Code
The Project Structure
The IAnimal Interface
This defines the contract to be use by the Animals class.
IAnimal.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace FactoryPattern.Interface | |
{ | |
public interface IAnimal | |
{ | |
void Talk(); | |
} | |
} |
The Animal Factory
This is the factory that decides what kind of animal will be created and pass it to the client. It sets between the client and the animal classes.
AnimalFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FactoryPattern.Animals; | |
using FactoryPattern.Interface; | |
namespace FactoryPattern.Factory | |
{ | |
public class AnimalFactory | |
{ | |
public IAnimal SetName(string Name) { | |
IAnimal theAnimal = null; | |
string _NameLowerCase = Name.ToLower(); | |
switch (_NameLowerCase) | |
{ | |
case "dog" : | |
theAnimal = new Dog(); | |
break; | |
case "cat": | |
theAnimal = new Cat(); | |
break; | |
case "chicken": | |
theAnimal = new Chicken(); | |
break; | |
case "duck": | |
theAnimal = new Duck(); | |
break; | |
} | |
return theAnimal; | |
} | |
} | |
} |
The Animal Classes
These are the classes that implements the IAnimal interface. These classes might grow large so that the approach in the Animal Factory is not suitable in large project. A better approach is by using reflection which I will be discussing later.
Dog.cs
Cat.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FactoryPattern.Interface; | |
using System; | |
namespace FactoryPattern.Animals | |
{ | |
class Dog : IAnimal | |
{ | |
public void Talk() | |
{ | |
Console.WriteLine("Aw aw!"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FactoryPattern.Interface; | |
using System; | |
namespace FactoryPattern.Animals | |
{ | |
public class Cat : IAnimal | |
{ | |
public void Talk() | |
{ | |
Console.WriteLine("Meow Meow"); | |
} | |
} | |
} |
I will only show 2 classes here because the rest are the same.
The Client Program
In the client, we can now instantiate Animal Factory and use the methods through IAnimal interface.
Program.cs
The Problems in this approach:
We can use reflection to solve this problem. Loop through the executing assembly and check if the class exist, then create the instance.
Don't Forget these:
Instead of using AnimalFactory.cs, use AnimalFactoryUsingReflection.cs.
AnimalFactoryUsingReflection.cs
In the client program, disregard the AnimalFactory.
Program.cs
The Client Program
In the client, we can now instantiate Animal Factory and use the methods through IAnimal interface.
Program.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FactoryPattern.Interface; | |
using System; | |
using FactoryPattern.Factory; | |
namespace FactoryPattern | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
AnimalFactory Animal = new AnimalFactory(); | |
//Dog | |
IAnimal Dog = Animal.SetName("Dog"); | |
Dog.Talk(); | |
//Cat | |
IAnimal Cat = Animal.SetName("Cat"); | |
Cat.Talk(); | |
//Cat | |
IAnimal Chicken = Animal.SetName("Chicken"); | |
Chicken.Talk(); | |
} | |
} | |
} |
The Problems in this approach:
- We did not eliminate the use of switch keyword.
- When a new class will be added in the future, we need to add more case statement in the AnimalFactory class, thus violating the Open Closed Principle.
We can use reflection to solve this problem. Loop through the executing assembly and check if the class exist, then create the instance.
Don't Forget these:
- Put the class in one folder. In this example, the folder is Animals.
- Be sure to convert the class name to lowercase before using it.
Instead of using AnimalFactory.cs, use AnimalFactoryUsingReflection.cs.
AnimalFactoryUsingReflection.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FactoryPattern.Animals; | |
using FactoryPattern.Interface; | |
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
namespace FactoryPattern.Factory | |
{ | |
class AnimalFactoryUsingReflection | |
{ | |
Dictionary<string, Type> animals; | |
public AnimalFactoryUsingReflection() | |
{ | |
LoadTypesICanReturn(); | |
} | |
public IAnimal CreateInstance(string animalName) | |
{ | |
Type t = GetTypeToCreate(animalName.ToLower()); | |
if (t == null) | |
{ | |
return new NullAnimal(); | |
} | |
return Activator.CreateInstance(t) as IAnimal; | |
} | |
Type GetTypeToCreate(string animalName) | |
{ | |
foreach (var animal in animals) | |
{ | |
if (animal.Key.Contains(animalName)) | |
{ | |
return animals[animal.Key]; | |
} | |
} | |
return null; | |
} | |
void LoadTypesICanReturn() | |
{ | |
animals = new Dictionary<string, Type>(); | |
Type[] typesInThisAssembly = Assembly.GetExecutingAssembly().GetTypes(); | |
foreach (Type type in typesInThisAssembly) | |
{ | |
if (type.GetInterface(typeof(IAnimal).ToString()) != null) | |
{ | |
animals.Add(type.Name.ToLower(),type); | |
} | |
} | |
} | |
} | |
} |
In the client program, disregard the AnimalFactory.
Program.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FactoryPattern.Interface; | |
using System; | |
using FactoryPattern.Factory; | |
namespace FactoryPattern | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Console.WriteLine("==================== Using Reflection ======================="); | |
Console.WriteLine("Avoiding Too much switch statement when classes grow."); | |
Console.WriteLine("============================================================="); | |
AnimalFactoryUsingReflection AnimalUsingReflection = new AnimalFactoryUsingReflection(); | |
IAnimal DogF = AnimalUsingReflection.CreateInstance("Dog"); | |
DogF.Talk(); | |
} | |
} | |
} |