Introduction
Factory pattern is very common in programming. If you check .NET Framework or any other frameworks, Factory Pattern is widely used which shows its popularity. Factory Pattern belongs to Creational Patterns, and it deals with object creation. In this article, we shall try to learn Factory Pattern using a simple C# console application.
Background
When we write code, we come across situations like creating objects based on some if
conditions or switch
case. If this object creation is not based on a proven design pattern, then we are really making the object creation complex and future enhancements very tough. At this time, we should consider Factory Pattern to abstract the complexity of the object creation logic, and enabling future additions hassle free.
Let's take a very simple example of logging functionality in a project. If we had the implementation as below, then adding a new Logging option requires considerable code changes at client code base which is not a good practice.
public void Log(string message, string type)
{
switch (type)
{
case "Database":
DatabaseLogger databaseLogger = new DatabaseLogger();
databaseLogger.Log(message);
break;
case "EventViewer":
default:
EventViewerLogger eventviewerLogger = new EventViewerLogger();
eventviewerLogger.Log(message);
break;
}
}
Factory Pattern is nothing but hiding object creation logic from the client so that the end client doesn't have to worry about object creational logic, rather the client can refer to the factory pattern created object using a common interface.
Using the Code
For better understanding, we can create a simple Logger Factory which will help the client to log messages to different options based on their choice.
To start with, we need to create an Interface or abstract
class as the base class for all classes which we are going to create instance in factory. Here, I have used an Interface ILogger
with a method Log
message.
interface ILogger
{
void Log(string Message);
}
Now, we are going to implement this ILogger
in all classes which we wanted to return from Logger Factory. Here, ideally Log
method should have the real method to log the message to a file but for demonstrating purposes, I'm just adding a console message.
class FileLogger : ILogger
{
public void Log(string Message)
{
Console.WriteLine("{0} - Logged in File.", Message);
}
}
the same way implementing ILogger
to DatabaseLogger
, also Log
method definition added.
class DatabaseLogger : ILogger
{
public void Log(string Message)
{
Console.WriteLine("{0} - Logged in Database.", Message);
}
}
EventViewerLogger
also implemented from ILogger
and Log
method definition added based on the type. We can add new logger classes which are the same as these logger classes.
class EventViewerLogger : ILogger
{
public void Log(string Message)
{
Console.WriteLine("{0} - Logged in EventViewer.", Message);
}
}
Create an enum
to easily identify the LoggerType
, In case if we have a new LoggerType
, then we can add it here.
enum LoggerType
{
File,
Database,
EventViewer
}
FInally, we have reached the LoggerFactory
. Now this factory will take care of the object creation based on the enum
values, and will return the created instance back to client code which will be a type of ILogger
.
class LoggerFactory
{
public static ILogger Get(LoggerType type)
{
switch (type)
{
case LoggerType.Database:
return new DatabaseLogger();
case LoggerType.EventViewer:
return new EventViewerLogger();
case LoggerType.File:
default:
return new FileLogger();
}
}
}
If you look at the above code, you can see that the object creation logic is abstracted in the factory and the objects are created based the kind of object requested, as the return type is an Interface ILogger
, the client code need not worry about new addition to the factory.
I have written a client code to make use of this factory as below:
static void Main(string[] args)
{
ILogger logger1 = LoggerFactory.Get(LoggerType.Database);
logger1.Log("Message from Main");
ILogger logger2 = LoggerFactory.Get(LoggerType.File);
logger2.Log("Message from Main");
ILogger logger3 = LoggerFactory.Get(LoggerType.EventViewer);
logger3.Log("Message from Main");
Console.ReadKey();
}
In the above code, the client code uses the enum
to request a specific object and using that object, the log
method is called. Objects are created using ILogger
and instantiated using factory. I have given the output below:
To explore this sample yourself, you can download the attached code or create a console project named "FactoryPatternSample
" and replace the content of Program.cs with the below code block.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryPatternSample
{
class Program
{
interface ILogger
{
void Log(string Message);
}
enum LoggerType
{
File,
Database,
EventViewer
}
class FileLogger : ILogger
{
public void Log(string Message)
{
Console.WriteLine("{0} - Logged in File.", Message);
}
}
class DatabaseLogger : ILogger
{
public void Log(string Message)
{
Console.WriteLine("{0} - Logged in Database.", Message);
}
}
class EventViewerLogger : ILogger
{
public void Log(string Message)
{
Console.WriteLine("{0} - Logged in EventViewer.", Message);
}
}
class LoggerFactory
{
public static ILogger Get(LoggerType type)
{
switch (type)
{
case LoggerType.Database:
return new DatabaseLogger();
case LoggerType.EventViewer:
return new EventViewerLogger();
case LoggerType.File:
default:
return new FileLogger();
}
}
}
static void Main(string[] args)
{
ILogger logger1 = LoggerFactory.Get(LoggerType.Database);
logger1.Log("Message from Main");
ILogger logger2 = LoggerFactory.Get(LoggerType.File);
logger2.Log("Message from Main");
ILogger logger3 = LoggerFactory.Get(LoggerType.EventViewer);
logger3.Log("Message from Main");
Console.ReadKey();
}
}
}
Summary
In this article, I have explained Factory Pattern with a simple C# application. I hope you have enjoyed this article and got some value addition to your knowledge. Please don't forget to mark your votes, suggestions and feedback to improve the quality of this and upcoming articles.
History
- 31st January, 2016: Initial version