Factory Design Patterns in C#

software developer, web developer, programmer, software engineer, technology, tech, web developer, programmer, programmer, software engineer, technology, technology, technology, tech, tech, tech, tech, tech

When building scalable and maintainable software systems, managing object creation properly becomes critical. Directly instantiating objects using the new keyword can tightly couple client code to concrete implementations, making systems harder to extend and maintain.

Factory patterns solve this problem by encapsulating object creation logic.

In this article, we’ll clearly understand:

  • Simple Factory
  • Factory Method
  • Abstract Factory

With real-world examples in C#.


Why Do We Need Factory Patterns?

Consider a payment system that supports:

  • UPI
  • Credit Card
  • Net Banking

If the client directly creates objects:

IPayment payment = new CreditCardPayment();

The client:

  • Knows the concrete class
  • Handles creation logic
  • Becomes tightly coupled to implementation

If tomorrow a new payment type is introduced, client code must change.

This violates the Open/Closed Principle.

Factory patterns move object creation responsibility away from the client.

Simple Factory Pattern

What Is It?

Simple Factory centralizes object creation in one class using conditional logic (if/switch).

It is not an official GoF pattern but widely used in practice.


Real-World Example: E-commerce Checkout

User selects payment type at checkout.

Interface

public interface IPayment
{
    void Process(decimal amount);
}

Implementations

public class UpiPayment : IPayment
{
    public void Process(decimal amount)
        => Console.WriteLine("Processing UPI payment");
}

public class CardPayment : IPayment
{
    public void Process(decimal amount)
        => Console.WriteLine("Processing Card payment");
}

Factory

public class PaymentFactory
{
    public static IPayment Create(string type)
    {
        return type switch
        {
            "UPI" => new UpiPayment(),
            "Card" => new CardPayment(),
            _ => throw new ArgumentException("Invalid payment type")
        };
    }
}

Client

var payment = PaymentFactory.Create(userSelection);
payment.Process(1000);

Advantages

  • Centralized object creation
  • Reduces client coupling
  • Easy to understand

Limitation

Adding a new payment type requires modifying the factory.
This partially violates Open/Closed Principle.

Best for small systems.


Factory Method Pattern

What Is It?

Factory Method defines an abstract method for creating objects. Subclasses decide which concrete implementation to instantiate.

Object creation is delegated to subclasses.


Real-World Example: Report Generation Framework

Suppose you’re building a reporting framework supporting:

  • PDF reports
  • Excel reports

Product Interface

public interface IReport
{
    void Export();
}

Concrete Products

public class PdfReport : IReport
{
    public void Export()
        => Console.WriteLine("Exporting PDF Report");
}

Abstract Creator

public abstract class ReportGenerator
{
    public abstract IReport CreateReport();

    public void Generate()
    {
        var report = CreateReport();
        report.Export();
    }
}

Concrete Creator

public class PdfReportGenerator : ReportGenerator
{
    public override IReport CreateReport()
        => new PdfReport();
}

Why Is This Better?

  • No switch-case
  • Adding new report type requires creating a new subclass
  • Better adherence to Open/Closed Principle
  • Ideal for extensible frameworks

Abstract Factory Pattern

What Is It?

Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes.

It creates multiple related objects together.


Real-World Example: Multi-Region SaaS Application

Suppose your product operates in:

  • India
  • USA

Each region requires:

  • Payment service
  • SMS service

India:

  • Razorpay
  • Local SMS Gateway

USA

  • Stripe
  • Twilio

These are related service families.


Abstract Products

public interface IPaymentService
{
    void Pay();
}

public interface ISmsService
{
    void SendSms();
}

Abstract Factory

public interface IServiceFactory
{
    IPaymentService CreatePaymentService();
    ISmsService CreateSmsService();
}

India Factory

public class IndiaServiceFactory : IServiceFactory
{
    public IPaymentService CreatePaymentService()
        => new RazorpayPaymentService();

    public ISmsService CreateSmsService()
        => new IndiaSmsService();
}

USA Factory

public class USServiceFactory : IServiceFactory
{
    public IPaymentService CreatePaymentService()
        => new StripePaymentService();

    public ISmsService CreateSmsService()
        => new TwilioSmsService();
}

Client Code

IServiceFactory factory = new IndiaServiceFactory();

var payment = factory.CreatePaymentService();
var sms = factory.CreateSmsService();

Why Use Abstract Factory?

  • Ensures related objects are used together
  • Clean separation of environments
  • Ideal for multi-tenant, multi-region, enterprise systems
  • Highly scalable architecture

PatternCreation ControlSwitch UsedSupports FamiliesOCP Compliance
Simple FactoryCentral classYesNoPartial
Factory MethodSubclassesNoNoGood
Abstract FactoryFactory of factoriesNoYesString

When Should You Use Each?

Use Simple Factory when:

  • System is small
  • Limited object types
  • Quick implementation needed

Use Factory Method when:

  • Designing frameworks
  • Expecting extensions
  • Want subclass-based control

Use Abstract Factory when:

  • Working with related object families
  • Supporting multiple environments
  • Building enterprise-level systems

Final Thoughts

Factory patterns are not about avoiding the new keyword.
They are about controlling object creation, reducing coupling, and designing extensible systems.

In modern .NET applications, Dependency Injection containers internally implement variations of Factory and Abstract Factory patterns.

Understanding when to use each pattern is what separates intermediate developers from senior engineers.

Thanks for reading

Read more here

Recommended Topics

Popular Tags

.net .NET 6 .Net Core .NET Developers .NET Development Future .NET Productivity .NET programming agentic ai agentic ai in .net AI Agents AI coding ai in dot net AI Tools .NET app.Map ASP.NET Core Azure AI Boilerplate C# C# AI C# Programming circuit breaker pattern Code Assistants Coding Coding in AI Creational Design Patterns Design Patterns dot net dotnet core resilience Factory Design Patterns in C# future of coding future of dot net Generative AI Intelligent coding tools Knowledge Lightweight API LLMs .NET Machine Learning .NET MapGet Microservices Middleware in .Net Core Minimal API ML.NET Motivational polly v8 resilience architecture REST API Semantic Kernel The Avinash Joshi TheAvinashJoshi Top 5 AI tools trending coding methods vibe coding Visual Studio AI Web API Web Development