In object oriented programming, whenever you want to access methods, properties or any members of an instance class, we need create object of that particular class. Using the instance of that class only, we can interact with that class. But creating the instance can be done from different places. Usually, we will do it from the calling class. But there are some design patterns which help us to create the instances in a better way to achieve some best practices in software development. In that list, Factory Pattern is one of the pattern. In this answer, I have explained, Simple Factory Pattern and Factory Method Pattern.
Simple Factory Pattern
The concept behind the Factory pattern is to creating instance of the concrete classes should be separated from the calling class. That should be the responsibility of the factory. Also, factory should return the interface not the concrete class.
So, when should you use Factory Pattern?
If the application has to find the concrete class during runtime, to work with, based on some particular condition or the parameter passed by the calling function,
If you creates instance of concrete classes based on if and else condition
If you creating instance based on the switch condition
What are the benefits by using this design pattern?
Separates the instance creation of concrete classes, from the client which uses that class.
Allows achieving Solid principles of software like Single Responsibility Principle, Open Close Principle.
Giving the extensibility to add new class without affecting the existing classes.
Allows writing unit testing for each classes by creating fake classes to test the functionality.
Allows achieving separation of concern
Please look at the below example, where we are creating instances of the concrete classes, based on the value passed by the user.The code may look very simple, but we are violating the open close principle. Because, if you need to add a new shape, we need to modify this if condition. But, as per the open close principle, a class should be modified only for extension, not for modification.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please type any one option from below list");
Console.WriteLine("Square");
Console.WriteLine("Rectangle");
string option = Console.ReadLine();
if (!String.IsNullOrEmpty(option))
{
if (option.ToLower() == "square")
{
Square square = new Square();
Console.WriteLine(square.Area());
}
else if(option.ToLower() == "rectangle")
{
Rectangle rectangle = new Rectangle();
Console.WriteLine(rectangle.Area());
}
}
Console.Read();
}
}
public class Square
{
public string Area() { return "Square area calculated..."; }
}
public class Rectangle
{
public string Area() { return "Rectangle area calculated..."; }
}
Simple Factory Pattern will help us to resolve this issue. In Simple Factory Pattern, Factory (SimpleShapeFactory) is a concrete class. It does not implement any interface. But, there will be a common interface (IShape), implemented by all the concrete classes (Square, Rectangle and Triangle) with which the client (Console Application) wanted to work with. So, when the Factory finds the matching class, it will return the instance of type implemented interface (IShape).
Once the class implements IShape, we are making sure that the class has a method (Area) with same name and signature as we have in interface(IShape).
How Factory finds the concrete class?
Factory will be using reflection concept (This is not the only way to find the concrete instance) to find the matching concrete class. What it does is, it will go through the current executing assembly and check each and every class which is implementing IShape interface also the class name should match with the value passed to the Factory.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please type any one option from below list");
Console.WriteLine("Square");
Console.WriteLine("Rectangle");
string option = Console.ReadLine();
IShape shape = new SimpleShapeFactory().GetInstance(option);
Console.WriteLine(shape.Area());
Console.Read();
}
}
public interface IShape
{
string Area();
}
public class Square : IShape
{
public string Area() { return "Square area calculated..."; }
}
public class Rectangle : IShape
{
public string Area() { return "Rectangle area calculated...";}
}
public class SimpleShapeFactory
{
public IShape GetInstance(string shape)
{
Type matchedShape = Assembly.GetExecutingAssembly().GetTypes().ToList().Where(x => x.IsClass && x.GetInterface("IShape") != null && x.Name.ToString().ToLower() == shape.ToLower()).FirstOrDefault();
if (matchedShape != null)
{
return (IShape)Activator.CreateInstance(matchedShape);
}
return new NoShape();
}
}
Factory Method Pattern:
Factory method pattern is almost same as Simple Factory Pattern. The difference is, Factory will not just be a concrete class, but also implement an interface. The interface will have a method that will be called by the calling class. So, there will be multiple Factory classes which do know how to create its sub class. But, this factory will know how to create instance for only one sub class. (This is the main difference between Factory Method Pattern and Abstract Factory Pattern) So calling class will get the right factory first and the factory will create the instance of subclass.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please type any one option from below list");
Console.WriteLine("Square");
Console.WriteLine("Rectangle");
string option = Console.ReadLine();
IShapeFactory shapeFactory = LoadFactory(option);
IShape shape = shapeFactory.CreateShape();
Console.WriteLine(shape.Area());
Console.Read();
}
private static IShapeFactory LoadFactory(string shape)
{
Type matchedShape = Assembly.GetExecutingAssembly().GetTypes().ToList().Where(x => x.IsClass && x.GetInterface("IShapeFactory") != null && x.Name.ToString().ToLower() == shape.ToLower() + "factory").FirstOrDefault();
if (matchedShape != null)
{
return (IShapeFactory)Activator.CreateInstance(matchedShape);
}
return new NoShapeFactory();
}
}
public interface IShapeFactory
{
IShape CreateShape();
}
public class SquareFactory : IShapeFactory
{
public IShape CreateShape() { return new Square(); }
}
public class RectangleFactory : IShapeFactory
{
public IShape CreateShape() { return new Rectangle(); }
}
If you use any of these pattern, when you need add a new shape like Circle, all you need to do is, just to add a new class for Circle, implement IShape, and provide the option for the user.
Note:
With the way we implemented out code in Factory to identify the shape class, we need to make sure the option what we provide for the use should match with the class name. Because, that is one of the condition added in factory to find the shape. You can use some different method to resolve this issue.