🏗️ Creational

Abstract Factory Pattern

Provides an interface for creating families of related or dependent objects without specifying their concrete classes. Abstract Factory is a super-factory that creates other factories, allowing you to produce objects that belong together without coupling your code to their concrete implementations.

Problem

When your application must work with multiple families of related products (e.g., UI components for different operating systems), creating objects directly would scatter platform-specific code throughout your application. Adding a new product family or a new product type requires changing many places, creating fragile and unmaintainable code.

Solution

Declare interfaces for each distinct product in the product family, then create an abstract factory interface with creation methods for all products. For each variant of a product family, create a separate factory class that returns products of a particular kind. The client code works with factories and products only through their abstract interfaces.

Participants

RoleResponsibility
AbstractFactoryDeclares an interface for operations that create abstract product objects
ConcreteFactoryImplements the operations to create concrete product objects for a specific family
AbstractProductDeclares an interface for a type of product object
ConcreteProductDefines a product object created by the corresponding concrete factory
ClientUses only interfaces declared by AbstractFactory and AbstractProduct classes

TypeScript Example

TypeScript
interface Button {
  render(): string;
}

interface Checkbox {
  check(): string;
}

interface UIFactory {
  createButton(): Button;
  createCheckbox(): Checkbox;
}

class DarkButton implements Button {
  render() { return "<button class='dark'>Click</button>"; }
}
class DarkCheckbox implements Checkbox {
  check() { return "<input type='checkbox' class='dark'/>"; }
}

class LightButton implements Button {
  render() { return "<button class='light'>Click</button>"; }
}
class LightCheckbox implements Checkbox {
  check() { return "<input type='checkbox' class='light'/>"; }
}

class DarkThemeFactory implements UIFactory {
  createButton() { return new DarkButton(); }
  createCheckbox() { return new DarkCheckbox(); }
}

class LightThemeFactory implements UIFactory {
  createButton() { return new LightButton(); }
  createCheckbox() { return new LightCheckbox(); }
}

function buildUI(factory: UIFactory) {
  const btn = factory.createButton();
  const cb = factory.createCheckbox();
  return `${btn.render()} ${cb.check()}`;
}

console.log(buildUI(new DarkThemeFactory()));

Real-World Example

Cross-platform UI toolkits like Qt or Java Swing use Abstract Factory. A toolkit defines an abstract factory for creating widgets (buttons, menus, scrollbars). Each supported platform provides a concrete factory that produces native-looking widgets. The application code only uses abstract widget interfaces, so switching from Windows to macOS requires only swapping the factory, not changing any application logic.

When to Use

  • Your system must be independent of how its products are created and composed
  • You need to enforce that related products are used together
  • You want to provide a library of products revealing only their interfaces, not implementations
  • You need to support multiple product families and want to switch between them easily

When Not to Use

  • You only have one product family and don't expect to add more
  • Product families don't share common interfaces
  • Adding the pattern to an existing system requires significant refactoring with little benefit

Understanding the Abstract Factory Pattern

The Abstract Factory pattern is one of the most important creational design patterns in object-oriented software design. Originally cataloged by the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) in their seminal 1994 book "Design Patterns: Elements of Reusable Object-Oriented Software," this pattern continues to be a cornerstone of modern software architecture.

Provides an interface for creating families of related or dependent objects without specifying their concrete classes. Abstract Factory is a super-factory that creates other factories, allowing you to produce objects that belong together without coupling your code to their concrete implementations. The pattern involves the following key participants: AbstractFactory, ConcreteFactory, AbstractProduct, ConcreteProduct, Client. Each participant has a well-defined role, and the interactions between them create a flexible, maintainable structure that can evolve with changing requirements.

In TypeScript and modern JavaScript development, the Abstract Factory pattern is particularly valuable because the language's type system and interface support allow you to express the pattern with compile-time safety. TypeScript generics can make Abstract Factory implementations even more flexible and reusable while maintaining strong type checking throughout the pattern's structure.

Knowing when to apply the Abstract Factory pattern is as important as knowing how. Common use cases include: Your system must be independent of how its products are created and composed; You need to enforce that related products are used together; You want to provide a library of products revealing only their interfaces, not implementations. However, overusing design patterns can lead to unnecessarily complex code. Always evaluate whether the pattern genuinely solves your problem or if a simpler approach would suffice.

The Abstract Factory pattern is related to several other design patterns: Factory Method, Builder, Prototype, Singleton. Understanding these relationships helps you choose the right pattern for your specific situation and combine patterns effectively when building complex systems.

Related Patterns

More Creational Patterns

Explore All Design Patterns

Browse our complete reference of 23 design patterns with TypeScript examples, UML participants, and practical guidance.