Abstract Factory Pattern in C#: Definition, Examples, Pros & Cons

Abstract Factory Pattern in C#: Definition, Examples, Pros & Cons

The Abstract Factory pattern in C# provides an interface for creating families of related or dependent objects without specifying their concrete classes.

The Abstract Factory pattern is a creational design pattern used to encapsulate object creation for related families of products. Instead of instantiating classes directly, client code works with abstract interfaces, making it independent of specific implementations. Each concrete factory produces a consistent set of related objects that are designed to work together. This pattern promotes consistency across products and enforces a contract for object creation. It is commonly used in applications that need to support multiple configurations or themes, such as UI frameworks or cross-platform systems.

Why We Use Abstract Factory Pattern in C#?

We use this pattern to decouple client code from concrete class implementations and to ensure that related objects are created together in a consistent way. It improves maintainability and scalability by centralizing object creation logic. It also supports the Open/Closed Principle, allowing new product families to be introduced without modifying existing code.

When to Use Abstract Factory Pattern in C#?

Use this pattern when your application needs to create families of related objects that must work together. It is especially useful when the system should be independent of how its objects are created and represented. It also fits scenarios where multiple configurations (e.g., themes, environments, or platforms) must be supported. If switching between object families should be easy and safe, Abstract Factory is a strong choice.

Example Use Cases with C# Code

1. Cross-Platform UI (Windows vs Mac)

// Abstract Products
public interface IButton
{
    void Render();
}

public interface ICheckbox
{
    void Render();
}

// Concrete Products (Windows)
public class WindowsButton : IButton
{
    public void Render() => Console.WriteLine("Render Windows Button");
}

public class WindowsCheckbox : ICheckbox
{
    public void Render() => Console.WriteLine("Render Windows Checkbox");
}

// Concrete Products (Mac)
public class MacButton : IButton
{
    public void Render() => Console.WriteLine("Render Mac Button");
}

public class MacCheckbox : ICheckbox
{
    public void Render() => Console.WriteLine("Render Mac Checkbox");
}

// Abstract Factory
public interface IUIFactory
{
    IButton CreateButton();
    ICheckbox CreateCheckbox();
}

// Concrete Factories
public class WindowsFactory : IUIFactory
{
    public IButton CreateButton() => new WindowsButton();
    public ICheckbox CreateCheckbox() => new WindowsCheckbox();
}

public class MacFactory : IUIFactory
{
    public IButton CreateButton() => new MacButton();
    public ICheckbox CreateCheckbox() => new MacCheckbox();
}

2. Database Providers (SQL Server vs MySQL)

public interface IDbConnection
{
    void Connect();
}

public interface IDbCommand
{
    void Execute();
}

public class SqlConnection : IDbConnection
{
    public void Connect() => Console.WriteLine("SQL Server Connected");
}

public class MySqlConnection : IDbConnection
{
    public void Connect() => Console.WriteLine("MySQL Connected");
}

public class SqlCommand : IDbCommand
{
    public void Execute() => Console.WriteLine("SQL Command Executed");
}

public class MySqlCommand : IDbCommand
{
    public void Execute() => Console.WriteLine("MySQL Command Executed");
}

public interface IDbFactory
{
    IDbConnection CreateConnection();
    IDbCommand CreateCommand();
}

public class SqlFactory : IDbFactory
{
    public IDbConnection CreateConnection() => new SqlConnection();
    public IDbCommand CreateCommand() => new SqlCommand();
}

public class MySqlFactory : IDbFactory
{
    public IDbConnection CreateConnection() => new MySqlConnection();
    public IDbCommand CreateCommand() => new MySqlCommand();
}

3. Theme System (Light vs Dark)

public interface IBackground
{
    string GetColor();
}

public interface IText
{
    string GetColor();
}

public class LightBackground : IBackground
{
    public string GetColor() => "White";
}

public class DarkBackground : IBackground
{
    public string GetColor() => "Black";
}

public class LightText : IText
{
    public string GetColor() => "Black";
}

public class DarkText : IText
{
    public string GetColor() => "White";
}

public interface IThemeFactory
{
    IBackground CreateBackground();
    IText CreateText();
}

public class LightThemeFactory : IThemeFactory
{
    public IBackground CreateBackground() => new LightBackground();
    public IText CreateText() => new LightText();
}

public class DarkThemeFactory : IThemeFactory
{
    public IBackground CreateBackground() => new DarkBackground();
    public IText CreateText() => new DarkText();
}

Advantages of Abstract Factory Pattern in C#

• Promotes consistency among related objects
• Decouples client code from concrete classes
• Makes switching product families easy
• Supports Open/Closed Principle
• Improves maintainability and scalability

Disadvantages of Abstract Factory Pattern in C#

• Increases complexity due to many interfaces and classes
• Harder to extend when adding new product types (requires changes to all factories)
• Can lead to over-engineering for simple scenarios
• Requires upfront design planning

Comparison with Similar Patterns

Pattern Purpose Key Difference Use Case
Abstract Factory Create families of related objects Produces multiple related objects UI themes, cross-platform systems
Factory Method Create a single object Uses inheritance for object creation When a class delegates instantiation
Builder Construct complex objects step by step Focuses on object construction process Complex object creation with many options
Singleton Ensure a single instance Controls instance count Shared resources like configuration