Back
Factory Method Design Pattern in C#

Factory Method Design Pattern in C#

The Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Key Concepts

  • Creator: Declares the factory method which returns new product objects
  • Concrete Creator: Overrides the factory method to return an instance of a ConcreteProduct
  • Product: Defines the interface of objects the factory method creates
  • Concrete Product: Implements the Product interface

When to Use

  • When you don't know beforehand the exact types and dependencies of the objects your code should work with
  • When you want to provide users of your library or framework with a way to extend its internal components
  • When you want to save system resources by reusing existing objects instead of rebuilding them each time

Implementation Example in C#

using System;

// Product interface
public interface IProduct
{
    string Operation();
}

// Concrete Products
public class ConcreteProduct1 : IProduct
{
    public string Operation()
    {
        return "{Result of ConcreteProduct1}";
    }
}

public class ConcreteProduct2 : IProduct
{
    public string Operation()
    {
        return "{Result of ConcreteProduct2}";
    }
}

// Creator abstract class
public abstract class Creator
{
    // The factory method
    public abstract IProduct FactoryMethod();
    
    public string SomeOperation()
    {
        // Call the factory method to create a Product object
        var product = FactoryMethod();
        // Now, use the product
        return $"Creator: The same creator's code has just worked with {product.Operation()}";
    }
}

// Concrete Creators
public class ConcreteCreator1 : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProduct1();
    }
}

public class ConcreteCreator2 : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProduct2();
    }
}

// Client code
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("App: Launched with ConcreteCreator1.");
        ClientCode(new ConcreteCreator1());
        
        Console.WriteLine("");
        
        Console.WriteLine("App: Launched with ConcreteCreator2.");
        ClientCode(new ConcreteCreator2());
    }
    
    public static void ClientCode(Creator creator)
    {
        Console.WriteLine("Client: I'm not aware of the creator's class," +
            "but it still works.\n" + creator.SomeOperation());
    }
}

Output

App: Launched with ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of ConcreteProduct1}

App: Launched with ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of ConcreteProduct2}

Advantages

  • Avoids tight coupling between creator and concrete products
  • Single Responsibility Principle - product creation code is moved to one place
  • Open/Closed Principle - new product types can be introduced without breaking existing code

Variations

  1. Parameterized Factory Method: The factory method can accept parameters to determine which product to create
  2. Lazy Initialization: The factory method can cache products and return existing instances instead of creating new ones

Real-world Example

// Document creator example
public interface IDocument
{
    void Open();
    void Save();
}

public class WordDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening Word document");
    public void Save() => Console.WriteLine("Saving Word document");
}

public class PdfDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening PDF document");
    public void Save() => Console.WriteLine("Saving PDF document");
}

public abstract class DocumentCreator
{
    public abstract IDocument CreateDocument();
    
    public void NewDocument()
    {
        var doc = CreateDocument();
        doc.Save();
    }
}

public class WordDocumentCreator : DocumentCreator
{
    public override IDocument CreateDocument() => new WordDocument();
}

public class PdfDocumentCreator : DocumentCreator
{
    public override IDocument CreateDocument() => new PdfDocument();
}

This pattern is widely used in .NET frameworks, such as in ASP.NET Core's dependency injection system where services are created using factory methods.

Comments - Beta - WIP

Leave a Comment