Skip to main content

Command Palette

Search for a command to run...

Design Patterns Simplified #3: Abstract Factory

Why did my order end up with a modern chair and a Victorian sofa?

Updated
8 min readView as Markdown

You're building the backend for a furniture store's website. Customers can order two things right now, a Chair and a Sofa. Both come in two styles: Modern and Victorian.

You start simple. A ChairFactory decides whether to build a ModernChair or a VictorianChair. Clean, works fine. This is just factory method and you already know it.

Then the catalog grows. Sofas get added. You do the same thing. A SofaFactory that builds either a ModernSofa or a VictorianSofa.

Both factories work correctly. Each one, on its own, does exactly what it's supposed to do.

Then a customer places an order for a "Modern Living Room Set" - one chair an one sofa. Whoever wired up the order-processing code grabs ChairFatory and asks for a Modern chair but accidentally grabs the Victorian version for the sofa because the two factories exist completely independently with nothing connecting them.

The order goes through without a single error. The customer just recieves a box with a sleek modern chair and a carved wooden victorian sofa because nothing in the code ever said, "if you're building for the modern collection, every piece of furnitue in this order must come from the modern family."

Each factory was correct in isolation. The problem is that nothing enforced that the whole group of furniture stayed consistent with each other. If you've ever had a bug where two related things - furniture styles, file formats, database drivers, cloud provider SDKs ended up mismatched because nothing tied them together as a set, you've already met the exact problem the Abstract Factory pattern exists to solve.

Why does this problem exist?

This problem shows up almost as a side effect of doing factory method correctly which is what makes it sneaky.

  • You start with one product and one factory that builds either style. This works great and is exactly the factory method.

  • More products get added over time. Later maybe Table, Bookshelf etc. each getting own independent factory.

  • Now you have a pile of separate, unrelated factories each one deciding modern VS victorian entirely on its own.

  • Nothing in the code groups "everything that belongs to the modern collection" together as one unit.

  • Whoever processes an order is now manually responsible for remembering to pick matching factories and pass the matching style to every single one of them. That is a purely human responsibility and human forget, copy-paste the wrong value or make a mistake during a rushed order.

The core issue: you have multiple factories that are supposed to move together as a family but nothing in your code actually models "family" as a concept. Each factory is consistent on its own but there's no guardrail keeping the group consistent.

The Concept

This is exactly the problem the Abstract Factory pattern (one of the Creational patterns) solves.

Official idea (from the Gang of Four):

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

In simple terms:

Instead of having separate, unrelated factories for each product, create one factory that knows how to build an entire matching set like a chair and a sofa, both from the same collection. Pick one factory and everything it gives you is guaranteed to belong together.

Where factory method gave you one method that creates one type of object, Abstract factory gives you one factory with several creation methods - one per product - all bundled together so they can't be mismatched.

Simple Explanation

What's the actual problem being solved? You have multiple families of related objects (the modern collection's chair and sofa, Victorian collection's chair and sofa) and nothing stops someone from accidentally mixing pieces from different families into the same order.

What does "family of related objects" mean here? A group of objects that are only meant to be used together. A modern chair should only ever ship alongside a modern sofa, never a victorian one. That "should only appear together" rule is exactly what Abstract factory encodes in code instead of leaving it as a rule someone has to remember.

How is this different from just using multiple factory methods? It isn't a totally different idea, it's the same idea, scaled up. Abstract factory is essentially several factory methods grouped into one interface, specificially so the group can be swapped together as a single unit instead of independently.

Real-World Analogy

This one barely needs a separate analogy - it is the example. Think of any store that sells matching sets. A furniture showroom, a paint store selling coordinated color palettes, a clothing store selling matching outfits.

You don't pick a chair from one collection and a sofa from a completely different one and hope they look good together in someone's living room. You pick one collection and the store hands you a chair and a sofa that are guaranteed to match - same wood tone, same era, designed to belong together.

That's the whole idea behind abstract factory. You ask for a "collection" and every item you get back is guaranteed to belong to that same collection.

// Defining the product interfaces (one per item type)
class Chair {
  public:
    virtual void describe() = 0;
    virtual ~Chair() = default;   
};

class Sofa {
  public:
    virtual void describe() = 0;
    virtual ~Sofa() = default;
};

// Create the concrete products, one set per collection
class ModernChair : public Chair {
  public:
    void describe() override {
        cout<<"A sleek Modern chair"<<endl;
    }
};

class VictorianChair : public Chair {
  void describe() override {
      cout<<"An ornate Victorian chair"<<endl;
  }  
};

class ModernSofa : public Sofa {
  public:
    void describe() override {
        cout<<"A sleek Modern sofa"<<endl;
    }
};

class VictorianSofa : public Sofa {
  void describe() override {
      cout<<"An ornate Victorian sofa"<<endl;
  }  
};

// Defining the abstract factory: one interface, one creation method per product
class FurnitureFactory {
  public:
    virtual Chair* createChair() = 0;
    virtual Sofa* createSofa() = 0;
    virtual ~FurnitureFactory() = default;
};

/* This is the key idea: FurnitureFactory bundles both creation methods together in one place, instead of leaving createChair() and createSofa() as two unrelated, independent factories.*/

/* Write one concrete factory per collection, each producing only its own matching products */
class ModernFurnitureFactory : public FurnitureFactory {
  public:
    Chair* createChair() override {
        return new ModernChair();
    }
    Sofa* createSofa() override {
        return new ModernSofa();
    }
};

class VictorianFurnitureFactory : public FurnitureFactory {
  public:
    Chair* createChair() override {
        return new VictorianChair();
    }
    Sofa* createSofa() override {
        return new VictorianSofa();
    }
};

/* ModernFurnitureFactory has no way to accidentally return a VictorianSofa — that class doesn't even appear anywhere inside it. */

/* The order-processing code just asks the factory for a full set, never picking items individually */
class OrderProcessor {
  public:
    void fulfillOrder(FurnitureFactory& factory){
        Chair* chair = factory.createChair();
        Sofa* sofa = factory.createSofa();
        
        chair->describe();
        sofa->describe();
        
        delete chair;
        delete sofa;
    }
};

int main(){
    OrderProcessor orders;
    
    ModernFurnitureFactory modernFactory;
    orders.fulfillOrder(modernFactory);
    
    VictorianFurnitureFactory victorianFactory;
    orders.fulfillOrder(victorianFactory);
}

Why this is better:

  • OrderProcessor never chooses individual items - it recieves one FurnitureFactory and trusts that everything it produces belongs together.

  • It's structurally impossible to ship a ModernChair with a VictorianSofa through this setup as there's no code path that follows it.

  • Adding a new collection (say "Industrial") means writing one new factory implementing all the creation methods but OrderProcessor doesn't change at all.

Isn't this just multiple factory methods combined into one class?

Pretty much and that's the right way to think about it. Abstract factory doesn't introduce a brand new creation mechanism, it takes the factory method idea and groups several of them into one interface, specificially so they travel together as a set instead of being picked independently.

What if only have one product, not a whole family?

Then you don't need abstract factory, plain factory method is the right tool. Abstract factory earns its place specificially when you have multiple related products that must say consistent as a group. One product, one factory method is already enough on its own.

What happens when I need to add a new product, like Table, to every collection?

You add one new method to the FurnitureFactory interface (createTable()) and every concrete factory (ModernFurnitureFactory, VictorianFurnitureFactory) is now required to implement it, the compiler will error out on any factory that forgets. That's actually a strength: the language itself enforces enforces that every collection stays complete.

Key Takeaways

If you remember only a few things from this article, remember these:

  • Abstract factory creates families of related objects, guaranteeing they stay consistent with each other.

  • The problem it solves: separate, unrelated factories that can accidentally be mixed and matched into an inconsistent combination.

  • It's essentially several factory methods bundled into a single interface.

  • Each concrete factory implements every method using only its own family's classes, mismatches become structurally impossible.

  • Adding a new family means one new factory class, adding a new product means one new method across every factory.

  • Use it when you have multiple related products that must travel together, not for single, unrelated objects.

From My Engineering Notebook

Abstract factory is not a new idea - it is just factory method, just zoomed out one level. Once I already understood "one method that decides which class to build", abstract factory was really just "okay, now group several of those together so they can't be separated."