Back to Blog

Coffee, Code, and Patterns: Factory vs Strategy

January 9, 2025Bobby Jose4 min read
Design Patterns
Software Architecture
C#
Python

When you first start reading about design patterns, some of them feel like they're just different names for the same idea.

Two of the biggest culprits:

  • Factory Pattern
  • Strategy Pattern

At first glance, both seem to deal with choosing something at runtime. But they're not the same. Let's break it down in a way that's actually memorable β€” with coffee β˜•.

Factory Pattern: Ordering the Coffee

Imagine you walk into Starbucks. You say:

"I'll take a Latte, please."

Do you know how to steam the milk, grind the beans, or draw the little foam heart on top? Nope. That's not your problem. The barista (factory) takes your request and gives you the right coffee (object).

That's the Factory Pattern.
It hides the messy "creation logic" and just hands you the object you asked for.

Example in C#

// Product Interface
public interface ICoffee
{
    void Serve();
}

public class Latte : ICoffee
{
    public void Serve() => Console.WriteLine("Serving a Latte");
}

public class Cappuccino : ICoffee
{
    public void Serve() => Console.WriteLine("Serving a Cappuccino");
}

// Factory
public class CoffeeFactory
{
    public static ICoffee CreateCoffee(string type)
    {
        return type switch
        {
            "latte" => new Latte(),
            "cappuccino" => new Cappuccino(),
            _ => throw new ArgumentException("Unknown coffee type")
        };
    }
}

// Usage
var coffee = CoffeeFactory.CreateCoffee("latte");
coffee.Serve(); // Serving a Latte

Example in Python

class Coffee:
    def serve(self):
        raise NotImplementedError

class Latte(Coffee):
    def serve(self):
        print("Serving a Latte")

class Cappuccino(Coffee):
    def serve(self):
        print("Serving a Cappuccino")

class CoffeeFactory:
    @staticmethod
    def create_coffee(type: str) -> Coffee:
        if type == "latte":
            return Latte()
        elif type == "cappuccino":
            return Cappuccino()
        else:
            raise ValueError("Unknown coffee type")

coffee = CoffeeFactory.create_coffee("latte")
coffee.serve()  # Serving a Latte

Takeaway:
Factory = Which coffee should I get?

Strategy Pattern: How You Drink It

Okay, now you've got your Latte. But here comes the next decision:

  • Do you add sugar?
  • Do you add honey?
  • Do you bravely drink it black like a true developer who hasn't slept in 3 days?

This is the Strategy Pattern: the coffee object stays the same, but the behavior of sweetening changes. You can swap one strategy for another without rewriting your whole coffee machine.

Example in C#

public interface ISweetener
{
    void AddSweetener();
}

public class Sugar : ISweetener
{
    public void AddSweetener() => Console.WriteLine("Adding Sugar");
}

public class Honey : ISweetener
{
    public void AddSweetener() => Console.WriteLine("Adding Honey");
}

public class CoffeeCup
{
    private ISweetener _sweetener;

    public CoffeeCup(ISweetener sweetener)
    {
        _sweetener = sweetener;
    }

    public void SetSweetener(ISweetener sweetener)
    {
        _sweetener = sweetener;
    }

    public void Drink()
    {
        Console.WriteLine("Coffee is ready...");
        _sweetener.AddSweetener();
    }
}

// Usage
var cup = new CoffeeCup(new Sugar());
cup.Drink(); // Coffee is ready... Adding Sugar

cup.SetSweetener(new Honey());
cup.Drink(); // Coffee is ready... Adding Honey

Example in Python

class Sweetener:
    def add(self):
        raise NotImplementedError

class Sugar(Sweetener):
    def add(self):
        print("Adding Sugar")

class Honey(Sweetener):
    def add(self):
        print("Adding Honey")

class CoffeeCup:
    def __init__(self, sweetener: Sweetener):
        self._sweetener = sweetener

    def set_sweetener(self, sweetener: Sweetener):
        self._sweetener = sweetener

    def drink(self):
        print("Coffee is ready...")
        self._sweetener.add()

cup = CoffeeCup(Sugar())
cup.drink()  # Coffee is ready... Adding Sugar

cup.set_sweetener(Honey())
cup.drink()  # Coffee is ready... Adding Honey

Takeaway:
Strategy = How should I customize my coffee?

The Cheat Sheet (Factory vs Strategy)

PatternWhat it decidesCoffee analogy
Factory PatternWhich object to createChoosing Latte vs Cappuccino
Strategy PatternWhich behavior to executeChoosing Sugar vs Honey

Final Sip

  • Use Factory when you need to create objects without hardcoding their classes.
  • Use Strategy when you need to swap algorithms/behaviors without touching the main object.

So next time someone asks you about the difference, just say:
πŸ‘‰ Factory is about which coffee you order, Strategy is about how you drink it.

And if they still don't get it… well, maybe they need more coffee β˜•.