Factory Design Pattern

The Factory Design Pattern falls under the Creational Design Patterns Category. The Factory Pattern is a design pattern used in object-oriented programming to create objects without specifying the exact class of object that will be created. The Factory Design Pattern is one of the most frequently used design patterns in real-time applications.

According to Gang of Four (GoF), a factory is an object used to create other objects, means a factory is a class with a method. That method creates and returns different objects based on the received input parameter.

In the Factory Design pattern, we create an object without exposing the Object Creation and Initialization logic to the client, and the client will refer to the newly created object using a common interface. It provides a way to delegate the instantiation logic to a factory class or method, allowing for more flexible and scalable code. Let's see the implementation with very basic example, then we will see the real world examples.

Implementation

Scenario

Let's say you are building a system that deals with different types of shapes (Circle, Rectangle, etc…). You want to use a factory to create these shape objects based on user input or other conditions.

Step 1 - Define an Interface for the Shape:

public interface IShape
{
    void Draw();
}

Step 2 - Implement Concrete Shapes:

public class Circle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Drawing a Circle");
    }
}

public class Rectangle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Drawing a Rectangle");
    }
}

Step 3 - Create a Factory Class:

public class ShapeFactory
{
    public IShape GetShape(string shapeType)
    {
        if (shapeType == null)
        {
            return null;
        }
        if (shapeType.Equals("CIRCLE"))
        {
            return new Circle();
        }
        else if (shapeType.Equals("RECTANGLE"))
        {
            return new Rectangle();
        }
        return null;
    }
}

Step 4 - Use the Factory to Get Shape Objects:

public class Program
{
    public static void Main(string[] args)
    {
        ShapeFactory shapeFactory = new  ShapeFactory();

        // Get an object of Circle and call its Draw method.
        IShape shape1 = shapeFactory.GetShape("CIRCLE");
        shape1.Draw();

        // Get an object of Rectangle and call its Draw method.
        IShape shape2 = shapeFactory.GetShape("RECTANGLE");
        shape2.Draw();
    }
}


Explanation

  • IShape Interface: This interface declares the Draw method that all shapes must implement.

  • Circle and Rectangle Classes: These are concrete implementations of the IShape interface.

  • ShapeFactory Class: This class has a method GetShape that returns an instance of a shape based on the shapeType string.

  • Program Class: In the Main method, you use the ShapeFactory to get instances of Circle and Rectangle and call their Draw methods.


Benefits

  • Encapsulation: The ShapeFactory encapsulates the object creation logic and keeps it separate from the main program.

  • Flexibility: Adding new shapes is easy. You just need to create a new class that implements IShape and update the ShapeFactory accordingly.

  • Decoupling: The Program class doesn't need to know about the concrete classes of shapes, just the IShape interface.

This simple example demonstrates how the Factory Design Pattern can be used to manage object creation and make code more flexible and maintainable. Now let's see some real-world examples of how the Factory Pattern might be applied.

Examples : Using Factory Design Pattern in C#

  1. Document Creation

    Consider a scenario where an application can generate different types of documents (PDF, Word, Excel etc….). You don't want to tightly couple the client code to specific document types.

    
    // Document interface
    public interface IDocument
    {
        void Print();
    }
    
    // Concrete Products
    public class PdfDocument : IDocument
    {
        public void Print()
        {
    	Console.WriteLine("Printing PDF Document");
        }
    }
    
    public class WordDocument : IDocument
    {
        public void Print()
        {
    	Console.WriteLine("Printing Word Document");
        }
    }
    
    // Factory interface
    public interface IDocumentFactory
    {
        IDocument CreateDocument();
    }
    
    // Concrete Factories
    public class PdfDocumentFactory : IDocumentFactory
    {
        public IDocument CreateDocument()
        {
    	    return new  PdfDocument(); 
        }
    }
    
    public class WordDocumentFactory : IDocumentFactory
    {
         public IDocument CreateDocument()
         {
    	     return new  WordDocument();
         }
    }
    // Client code
    class Program 
    {
        static void Main(string[] args)
        {
            IDocumentFactory factory = new  PdfDocumentFactory();
            IDocument document = factory.CreateDocument();
            document.Print();  // Output: Printing PDF Document
    
            factory = new  WordDocumentFactory();
            document = factory.CreateDocument();
            document.Print();  // Output: Printing Word Document
        }
    }
    
    
  2. Database Connection

    When an application needs to connect to different types of databases ( SQL Server, MySQL, Oracle etc…), a factory pattern can manage the creation of database connection objects.

    
    // Database Connection interface
    public interface IDatabaseConnection
    {
        void Connect();
    }
    
    // Concrete Class
    public class SqlServerConnection : IDatabaseConnection
    {
        public void Connect() 
        {
               Console.WriteLine("Connecting to SQL Server");
        }
    }
    
    public class MySqlConnection : IDatabaseConnection
    {
       public void Connect() 
       {
               Console.WriteLine("Connecting to MySql");
       }
    }
    public class OracleConnection : IDatabaseConnection
    {
        public void Connect() 
        {
               Console.WriteLine("Connecting to Oracle");
         }
    }
    
    // Factory interface
    public interface IDatabaseConnectionFactory
    {
        IDatabaseConnection CreateConnection();
    }
    
    // Concrete Factories
    public class SqlServerConnectionFactory : IDatabaseConnectionFactory
    {
        public IDatabaseConnection CreateConnection() 
        {
                return new SqlServerConnection();
        }
    }
    
    public class MySqlConnectionFactory : IDatabaseConnectionFactory
    {
         public IDatabaseConnection CreateConnection() 
         {
                return new MySqlConnection();
         }
    }
    
    public class OracleConnectionFactory : IDatabaseConnectionFactory
    {
         public IDatabaseConnection CreateConnection() 
         {
                return new OracleConnection();
         }
    }
    
    // Client code
    class Program
    {
        static void Main(string[] args)
        {
            IDatabaseConnectionFactory factory = new SqlServerConnectionFactory();
            IDatabaseConnection connection = factory.CreateConnection();
            connection.Connect();  // Output: Connecting to SQL Server
    
            factory = new MySqlConnectionFactory();
            connection = factory.CreateConnection();
            connection.Connect();  // Output: Connecting to MySQL
    
            factory = new OracleConnectionFactory();
            connection = factory.CreateConnection();
            connection.Connect();  // Output: Connecting to Oracle
        }
    }
    
    

When and Where to Use Factory Design Pattern

When to Use:

  • Complex Object Creation: When the creation process of an object is complex or requires multiple steps.

  • Decoupling: When you want to decouple the client code from the concrete implementations of the objects it needs.

  • Scalability: When you anticipate that new types of products might be added, and you want to minimize changes to the existing code.

Where to Use:
  • UI Components: For creating different types of UI controls dynamically.

  • Data Access Layers: When creating different types of data access objects or repositories.

  • Dependency Injection: When integrating with dependency injection frameworks, which often use factories to create and inject dependencies.


Summary

In each of these examples, the is used to handle the creation of objects in a way that allows for easy extension and maintenance. By delegating the creation process to a factory, you decouple the client code from the specifics of the classes it needs, making the system more flexible and easier to adapt to new requirements or changes.