Low-level design3 min

Abstract Factory

Type: Creational Pattern
Relevance: Medium/High. Commonly used in enterprise systems requiring families of related objects.

The Abstract Factory pattern lets you produce families of related objects without specifying their concrete classes. It is essentially a "Factory of Factories."

Real-Life Analogy

Think of a Furniture Store. A furniture store sells related families of items, such as Modern, Victorian, and ArtDeco. A family consists of a Chair, a Sofa, and a CoffeeTable. If a customer buys a Victorian Chair, they expect the Victorian Sofa to match it perfectly. You would use a VictorianFurnitureFactory to ensure all created products belong to the Victorian family and are compatible.

The Problem

If you use the standard Factory Method, you might accidentally mix a ModernChair with a VictorianSofa. The Abstract Factory guarantees that all products created by a specific factory variant are compatible with each other.

How to Implement Abstract Factory

  1. Explicitly declare interfaces for each distinct product in the product family (e.g., Chair, Sofa).
  2. Create the Abstract Factory interface, which declares creation methods for all products in the family (createChair(), createSofa()).
  3. Create concrete factory classes that implement the Abstract Factory interface for a specific variant (e.g., ModernFurnitureFactory, VictorianFurnitureFactory).

Example in Code

typescript
// 1. Abstract Products
interface Chair { hasLegs(): boolean; sitOn(): void; }
interface Sofa { hasCushions(): boolean; lieOn(): void; }

// Concrete Products (Victorian Family)
class VictorianChair implements Chair {
  hasLegs() { return true; }
  sitOn() { console.log("Sitting on a fancy Victorian chair."); }
}
class VictorianSofa implements Sofa {
  hasCushions() { return true; }
  lieOn() { console.log("Lying on a velvet Victorian sofa."); }
}

// Concrete Products (Modern Family)
class ModernChair implements Chair {
  hasLegs() { return false; } // It's a weird hover chair
  sitOn() { console.log("Sitting on a sleek Modern chair."); }
}
class ModernSofa implements Sofa {
  hasCushions() { return false; } // It's just a slab
  lieOn() { console.log("Lying on a minimalist Modern sofa."); }
}

// 2. Abstract Factory
interface FurnitureFactory {
  createChair(): Chair;
  createSofa(): Sofa;
}

// 3. Concrete Factories
class VictorianFurnitureFactory implements FurnitureFactory {
  createChair(): Chair { return new VictorianChair(); }
  createSofa(): Sofa { return new VictorianSofa(); }
}

class ModernFurnitureFactory implements FurnitureFactory {
  createChair(): Chair { return new ModernChair(); }
  createSofa(): Sofa { return new ModernSofa(); }
}

// Client Code
function orderLivingRoom(factory: FurnitureFactory) {
  const chair = factory.createChair();
  const sofa = factory.createSofa();
  
  chair.sitOn();
  sofa.lieOn();
}

// Usage
console.log("Ordering Victorian Furniture:");
orderLivingRoom(new VictorianFurnitureFactory());

console.log("\nOrdering Modern Furniture:");
orderLivingRoom(new ModernFurnitureFactory());

Class Diagram

Factory Method vs Abstract Factory

  • Factory Method: Creates exactly one type of product. Uses Inheritance (subclasses implement the factory method).
  • Abstract Factory: Creates families of related products. Uses Composition (factories are injected into the client). It actually usually uses the Factory Method inside its methods to create the products.