Abstract Factory Design Pattern in TypeScript

Mehmet Yildiz
4 min readSep 5, 2023

When it comes to modern manufacturing, we often talk about assembly lines. A specific assembly line is set up to produce a specific type of product, like a line dedicated to manufacturing SUVs and another for sedans. If you want to create an SUV, you’d use the SUV assembly line, and for a sedan, you’d use the sedan assembly line. Here, the assembly line acts as a factory for creating vehicles.

In software design, the Abstract Factory Pattern can be likened to this concept of assembly lines. It is a creational pattern that provides an interface for creating related or dependent objects without specifying their concrete classes.

Photo by carlos aranda on Unsplash

Setting the Stage for the Abstract Factory Pattern

The Abstract Factory Pattern allows a system to be independent of how its objects are created, composed, and represented. It involves multiple Factory Methods, one for each type of object to be created. When you extend the system to produce objects that conform to a new criterion, you don’t need to modify the existing code; you only need to add new factory methods.

Imagine a user interface toolkit that needs to create buttons, panels, and windows. You could use the Abstract Factory Pattern to ensure that all elements in the toolkit are consistent (e.g., all following a “dark mode” theme or a “light mode” theme) without specifying the exact classes to instantiate.

Why and When?

The Abstract Factory Pattern offers several benefits:

  1. Consistency: Ensures that the products (objects) produced are consistent in nature.
  2. Flexibility: Provides flexibility by isolating concrete classes and making it easy to exchange product families.
  3. Scalability: Promotes scalability by allowing new kinds of products to be incorporated without altering existing code.

You should consider using the Abstract Factory Pattern when:

  • The system needs to be independent of how its objects are created, composed, and represented.
  • The system needs to be configured with one of multiple families of objects.
  • You want to provide a library of products and reveal only their interfaces, not their implementations.

Constructing Objects with the Abstract Factory Pattern in TypeScript: A Step-by-Step Guide

Now, let’s dive into how we can apply the Abstract Factory Pattern using a simple UI toolkit scenario:

Step 1: Define Abstract Product and Factory Interfaces

// Abstract Products
interface Button {
click(): void;
}

interface Panel {
display(): void;
}

// Abstract Factory
interface UIAbstractFactory {
createButton(): Button;
createPanel(): Panel;
}

Step 2: Implement Concrete Products

// Concrete Product: Dark Mode
class DarkButton implements Button {
click(): void {
console.log("Clicked a dark button!");
}
}

class DarkPanel implements Panel {
display(): void {
console.log("Displaying a dark panel.");
}
}

// Concrete Product: Light Mode
class LightButton implements Button {
click(): void {
console.log("Clicked a light button!");
}
}

class LightPanel implements Panel {
display(): void {
console.log("Displaying a light panel.");
}
}

Step 3: Implement Concrete Factories

class DarkModeFactory implements UIAbstractFactory {
createButton(): Button {
return new DarkButton();
}

createPanel(): Panel {
return new DarkPanel();
}
}

class LightModeFactory implements UIAbstractFactory {
createButton(): Button {
return new LightButton();
}

createPanel(): Panel {
return new LightPanel();
}
}

Step 4: Use the Abstract Factory Pattern

// Client code
const factory: UIAbstractFactory = new DarkModeFactory();

const button: Button = factory.createButton();
button.click();

const panel: Panel = factory.createPanel();
panel.display();

This example demonstrates how to produce consistent UI elements using the Abstract Factory Pattern. By switching between DarkModeFactory and LightModeFactory, you can easily toggle between dark and light themes.

Distinguishing Between the Factory and Abstract Factory Patterns

While both the Factory and Abstract Factory patterns deal with object creation and aim to separate the client from the instantiation logic, understanding their differences is crucial for proper application.

Factory Pattern:

Purpose: The Factory Method Pattern provides an interface for creating an object but allows subclasses to alter the type of objects that will be created. It’s all about creating a single object.

Structure: Typically involves a single method with different implementations or overrides, returning different types of objects based on conditions or parameters.

Example: Consider a “Vehicle Factory” that produces either a car or a motorcycle based on the input. The single factory decides the type of vehicle to produce based on the parameters it receives.

Abstract Factory Pattern:

Purpose: The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. It deals with the creation of multiple related objects.

Structure: Focuses on multiple factory methods, with each method responsible for creating a different type of object. These methods are usually grouped into “families”.

Example: Revisiting the UI toolkit from earlier, the “Dark Mode Factory” can produce dark-themed buttons, panels, and windows, while the “Light Mode Factory” produces light-themed counterparts. Here, it’s not just about creating a single UI element but a consistent set of related UI elements.

In essence, while the Factory Pattern emphasizes a single product’s creation, the Abstract Factory Pattern emphasizes creating a suite of products. This distinction, although subtle, can lead to different design decisions, affecting the flexibility and scalability of your system. Always choose the pattern that best aligns with the specific needs of your application.

Wrapping Up

The Abstract Factory Pattern is instrumental in scenarios where a system should be decoupled from the actual creation of objects and should function with varying configurations. By using this pattern, you ensure consistency across product families and make your codebase more scalable and maintainable.

As with other design patterns, it’s essential to understand the context and need before implementing the Abstract Factory Pattern. Remember, design patterns offer solutions to recurring problems, but not every problem fits the mold. Always prioritize the requirements and simplicity of your application. Happy coding!

--

--