Building Themed Teams with the Abstract Factory Pattern in C#

Published on: March 17, 2025

Welcome back to our design pattern series.

If you’ve been following along, you’ve already mastered the Simple Factory Pattern and explored the Factory Method Pattern in my previous posts.

Now, we’re stepping into the third "flavor" of the factory family: the Abstract Factory Pattern. This one’s a powerhouse for creating families of related objects (like a character and their gear in a game) all tied together with a consistent theme.

You’ll see how it builds on what we’ve learned and takes flexibility to the next level. Let’s dive in!

The Problem: When Single Objects Aren’t Enough

Let’s revisit our example code from the Factory Method post.

We had a coffee shop making drinks like Espresso or Latte, each crafted by its own creator class. That worked great for single items. But now, imagine the shop expands to offer drink sets for customers, like a "Morning Boost" set with an Espresso, a Croissant, and a Small Cup, or an "Afternoon Chill" set with a Latte, a Muffin, and a Tall Cup. Each set needs to match: you wouldn’t pair a Latte with a stale biscuit meant for Espresso.

A naive approach might be to hardcode these combos in the main CoffeeShop class with a big switch or multiple if-else blocks. Adding a new set (like a "Night Owl") combo, means rewriting that logic every time.

Worse, if you reuse this creation code elsewhere (say, for a delivery app), you’re duplicating it, breaking the Single Responsibility Principle (SRP) from SOLID.

The Factory Method helped with individual drinks, but it’s not built for coordinated sets.

We need a pattern that handles related objects as a unit.

Naive Approach: A Tangled Mess

Here’s how we might stumble through this without a pattern:

public class CoffeeShop
{
    public void OrderCombo(string type)
    {
        if (type == "Morning")
        {
            Console.WriteLine("Espresso brewed!");
            Console.WriteLine("Croissant baked!");
            Console.WriteLine("Small cup filled!");
        }
        else if (type == "Afternoon")
        {
            Console.WriteLine("Latte crafted!");
            Console.WriteLine("Muffin baked!");
            Console.WriteLine("Tall cup filled!");
        }
        else
        {
            Console.WriteLine("No such combo!");
        }
    }
}

And your Program.cs file will look like:

var shop = new CoffeeShop();
shop.OrderCombo("Morning");

The Output:

Espresso brewed!
Croissant baked!
Small cup filled!

This is a mess. Adding a "Night Owl" set means more else-if branches, and there’s no guarantee the items stay consistent. It’s inflexible and screams for a refactor.

Introducing the Abstract Factory Pattern: Crafting Themed Sets

You’ve seen the Simple Factory centralize creation and the Factory Method delegate it to subclasses.

The Abstract Factory Pattern takes this further by creating families of related objects.

Think of it as a "set coordinator": it defines a blueprint for making a drink, a pastry, and a cup, then lets concrete factories produce themed sets, like "Morning Boost" or "Afternoon Chill."

Key differences from Factory Method:

  • Scope: Factory Method makes one object (e.g., a drink). Abstract Factory makes a set of related objects (drink + pastry + cup).
  • Structure: Instead of one factory method, you get multiple methods in an interface, each crafting part of the family.
  • Purpose: Ensures consistency across the set, like all "Morning" items matching, without hardcoding every combo.

The players:

  • An interface for the products (drink, pastry, cup).
  • An abstract factory interface with methods to create each product.
  • Concrete factories that build specific themed sets.

Let’s refactor our coffee shop into this pattern.

Applying the Abstract Factory Pattern: Refactoring Step-by-Step

We’ll extend the Factory Method idea into a full themed-set system.

Step 1: Define Product Interfaces

We need blueprints for each part of the combo:

public interface IDrink
{
    string GetDescription();
}

public interface IPastry
{
    string GetDescription();
}

public interface ICup
{
    string GetDescription();
}

Step 2: Create Concrete Products

Now, the specific items for each theme:

public class Espresso : IDrink { public string GetDescription() => "Espresso brewed!"; }
public class Croissant : IPastry { public string GetDescription() => "Croissant baked!"; }
public class SmallCup : ICup { public string GetDescription() => "Small cup filled!"; }

public class Latte : IDrink { public string GetDescription() => "Latte crafted!"; }
public class Muffin : IPastry { public string GetDescription() => "Muffin baked!"; }
public class TallCup : ICup { public string GetDescription() => "Tall cup filled!"; }

Step 3: Define the Abstract Factory

Here’s the factory interface for creating a whole set:

public interface ICoffeeSetFactory
{
    IDrink CreateDrink();
    IPastry CreatePastry();
    ICup CreateCup();
}

This is the big leap from Factory Method: multiple methods for a family, not just one.

Step 4: Implement Concrete Factories

Now, factories for each themed set:

public class MorningBoostFactory : ICoffeeSetFactory
{
    public IDrink CreateDrink() => new Espresso();
    public IPastry CreatePastry() => new Croissant();
    public ICup CreateCup() => new SmallCup();
}

public class AfternoonChillFactory : ICoffeeSetFactory
{
    public IDrink CreateDrink() => new Latte();
    public IPastry CreatePastry() => new Muffin();
    public ICup CreateCup() => new TallCup();
}

Adding a "Night Owl" set? Just make a new factory, no touching existing code.

Step 5: Use the Factory

Here’s the shop in action (in your Program.cs):

ICoffeeSetFactory factory = new MorningBoostFactory();
var drink = factory.CreateDrink();
var pastry = factory.CreatePastry();
var cup = factory.CreateCup();

Console.WriteLine(drink.GetDescription());
Console.WriteLine(pastry.GetDescription());
Console.WriteLine(cup.GetDescription());

Output:

Espresso brewed!
Croissant baked!
Small cup filled!
Switch to AfternoonChillFactory, and you get the Latte set—clean and consistent.

Real-World Scenarios: Where It Fits

Beyond coffee shops, Abstract Factory shines in these cases:

  • UI Themes: Building a "Dark Mode" set (button, window, scrollbar) or "Light Mode" set—all matching.
  • Game Character Kits: Creating a "Fire Wizard" (character, staff, robe) or "Ice Knight" (character, sword, armor).
  • Hardware Drivers: Producing a "Windows" set (keyboard, mouse, display) or "Mac" set, ensuring compatibility.

It’s perfect when you need a group of objects to work together seamlessly.

Code Smells: Signs You Need It

Look for these hints:

  • Scattered Creation Logic: Making related objects (drink, pastry) in different places? Centralize it.
  • Inconsistent Sets: Hardcoding combos that don’t always match? Abstract Factory ensures cohesion.
  • Complex Conditionals: Big switch blocks for multiple object types? This pattern simplifies it.

Wrapping Up

The Abstract Factory Pattern takes the Factory Method’s flexibility and scales it up to handle families of objects.

While Factory Method gave us one drink at a time, Abstract Factory delivers a full themed set (drink, pastry, cup) all in sync.

It’s a natural evolution for growing projects, keeping your code organized and extensible.


Happy Coding! ⚡


Design PatternsAbstract Factory PatternC#OOPSoftware Engineering