CreationalC#verifiedVerified
Abstract Factory Pattern in C#
Provides an interface for creating families of related objects without specifying their concrete classes.
How to Implement the Abstract Factory Pattern in C#
1Step 1: Define abstract product interfaces
public interface IButton
{
string Render();
}
public interface ICheckbox
{
string Render();
}2Step 2: Define the abstract factory
public interface IUIFactory
{
IButton CreateButton();
ICheckbox CreateCheckbox();
}3Step 3: Concrete products for Light theme
public class LightButton : IButton
{
public string Render() => "[Light Button]";
}
public class LightCheckbox : ICheckbox
{
public string Render() => "[Light Checkbox]";
}4Step 4: Concrete products for Dark theme
public class DarkButton : IButton
{
public string Render() => "[Dark Button]";
}
public class DarkCheckbox : ICheckbox
{
public string Render() => "[Dark Checkbox]";
}5Step 5: Concrete factories
public class LightThemeFactory : IUIFactory
{
public IButton CreateButton() => new LightButton();
public ICheckbox CreateCheckbox() => new LightCheckbox();
}
public class DarkThemeFactory : IUIFactory
{
public IButton CreateButton() => new DarkButton();
public ICheckbox CreateCheckbox() => new DarkCheckbox();
}6Step 6: Client works only with factory interface
public class Application(IUIFactory factory)
{
private readonly IButton _button = factory.CreateButton();
private readonly ICheckbox _checkbox = factory.CreateCheckbox();
public string RenderUI() =>
$"{_button.Render()} {_checkbox.Render()}";
}using Microsoft.Extensions.Logging;
// [step] Define abstract product interfaces for database access
public interface IDbConnection : IAsyncDisposable
{
Task<bool> OpenAsync(CancellationToken ct = default);
Task<IReadOnlyList<T>> QueryAsync<T>(
string sql, object? parameters = null,
CancellationToken ct = default);
}
public interface IDbCommand : IAsyncDisposable
{
Task<int> ExecuteAsync(
string sql, object? parameters = null,
CancellationToken ct = default);
}
public interface IDbTransaction : IAsyncDisposable
{
Task CommitAsync(CancellationToken ct = default);
Task RollbackAsync(CancellationToken ct = default);
}
// [step] Abstract factory for database provider
public interface IDatabaseFactory
{
string ProviderName { get; }
IDbConnection CreateConnection(string connectionString);
IDbCommand CreateCommand();
IDbTransaction CreateTransaction();
}
// [step] PostgreSQL concrete products
public class PostgresConnection(
string connectionString, ILogger logger) : IDbConnection
{
public async Task<bool> OpenAsync(CancellationToken ct = default)
{
logger.LogDebug("Opening PostgreSQL connection");
await Task.Delay(10, ct);
return true;
}
public Task<IReadOnlyList<T>> QueryAsync<T>(
string sql, object? parameters = null,
CancellationToken ct = default)
{
logger.LogDebug("PostgreSQL query: {Sql}", sql);
return Task.FromResult<IReadOnlyList<T>>(Array.Empty<T>());
}
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
public class PostgresCommand(ILogger logger) : IDbCommand
{
public Task<int> ExecuteAsync(
string sql, object? parameters = null,
CancellationToken ct = default)
{
logger.LogDebug("PostgreSQL execute: {Sql}", sql);
return Task.FromResult(0);
}
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
public class PostgresTransaction(ILogger logger) : IDbTransaction
{
public async Task CommitAsync(CancellationToken ct = default)
{
logger.LogDebug("PostgreSQL commit");
await Task.CompletedTask;
}
public async Task RollbackAsync(CancellationToken ct = default)
{
logger.LogWarning("PostgreSQL rollback");
await Task.CompletedTask;
}
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
// [step] PostgreSQL factory
public class PostgresFactory(ILogger<PostgresFactory> logger) : IDatabaseFactory
{
public string ProviderName => "PostgreSQL";
public IDbConnection CreateConnection(string connectionString) =>
new PostgresConnection(connectionString, logger);
public IDbCommand CreateCommand() => new PostgresCommand(logger);
public IDbTransaction CreateTransaction() => new PostgresTransaction(logger);
}
// [step] Client uses the abstract factory for provider independence
public class Repository(IDatabaseFactory factory, string connectionString)
{
public async Task<IReadOnlyList<T>> GetAllAsync<T>(
string table, CancellationToken ct = default)
{
await using var conn = factory.CreateConnection(connectionString);
await conn.OpenAsync(ct);
return await conn.QueryAsync<T>(
$"SELECT * FROM {table}", ct: ct);
}
}Abstract Factory Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Abstract Factory Pattern in the Real World
“Imagine furnishing a room from IKEA versus a luxury boutique. Each store (factory) produces a complete set of furniture — chairs, tables, sofas — that share a consistent style. You pick the store, and every piece you get matches. You never mix a rustic IKEA chair with a baroque boutique table.”