Factory Method Pattern in TypeScript: A Comprehensive Guide

Explore the Factory Method Pattern in TypeScript, a creational design pattern that provides an interface for creating objects, allowing subclasses to alter the type of objects that will be created. Learn how to implement this pattern to achieve flexibility and maintainability in your TypeScript applications.

4.2 Factory Method Pattern

The Factory Method Pattern is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. This pattern is particularly useful when a class cannot anticipate the class of objects it must create or when a class wants its subclasses to specify the objects it creates.

Understanding the Factory Method Pattern

Intent and Purpose

The primary intent of the Factory Method Pattern is to define an interface for creating an object, but let subclasses decide which class to instantiate. This pattern lets a class defer instantiation to subclasses, promoting flexibility and scalability in object-oriented design.

Problem Solved

In many scenarios, applications require flexibility in object creation. Hardcoding the instantiation of specific classes can lead to a rigid design that is difficult to extend or maintain. The Factory Method Pattern addresses this by allowing the creation logic to be encapsulated in a method, which can be overridden by subclasses to create specific types of objects. This approach adheres to the Open/Closed Principle, allowing the system to be open for extension but closed for modification.

Participants in the Factory Method Pattern

The Factory Method Pattern involves several key participants:

  1. Product: This is an interface or abstract class that defines the structure of the objects the factory method creates.
  2. ConcreteProduct: These are the implementations of the Product interface. Each ConcreteProduct corresponds to a specific type of object that the factory method can produce.
  3. Creator: This is an abstract class that declares the factory method, which returns an object of type Product. The Creator may also define a default implementation of the factory method that returns a default ConcreteProduct.
  4. ConcreteCreator: These are subclasses of the Creator that override the factory method to return an instance of a ConcreteProduct.

UML Class Diagram

To better understand the relationships between these participants, let’s visualize them using a UML class diagram:

    classDiagram
	    class Product {
	        <<interface>>
	    }
	    class ConcreteProductA {
	        +operation()
	    }
	    class ConcreteProductB {
	        +operation()
	    }
	    class Creator {
	        +factoryMethod() Product
	        +someOperation()
	    }
	    class ConcreteCreatorA {
	        +factoryMethod() ConcreteProductA
	    }
	    class ConcreteCreatorB {
	        +factoryMethod() ConcreteProductB
	    }
	
	    Product <|-- ConcreteProductA
	    Product <|-- ConcreteProductB
	    Creator <|-- ConcreteCreatorA
	    Creator <|-- ConcreteCreatorB
	    Creator o-- Product

How the Pattern Promotes Loose Coupling

The Factory Method Pattern promotes loose coupling by separating the code that creates objects from the code that uses them. This separation allows for flexibility in changing the types of objects created without affecting the client code. By adhering to the Open/Closed Principle, the pattern ensures that new product types can be introduced without modifying existing code, thus enhancing maintainability and scalability.

Implementing the Factory Method Pattern in TypeScript

Let’s explore how to implement the Factory Method Pattern in TypeScript with a practical example. We’ll create a simple application that generates different types of documents.

Step 1: Define the Product Interface

First, we define the Document interface, which represents the Product in our pattern.

1// Document.ts
2export interface Document {
3    print(): void;
4}

Step 2: Implement Concrete Products

Next, we implement two ConcreteProducts: PDFDocument and WordDocument.

 1// PDFDocument.ts
 2import { Document } from './Document';
 3
 4export class PDFDocument implements Document {
 5    print(): void {
 6        console.log('Printing PDF document...');
 7    }
 8}
 9
10// WordDocument.ts
11import { Document } from './Document';
12
13export class WordDocument implements Document {
14    print(): void {
15        console.log('Printing Word document...');
16    }
17}

Step 3: Create the Creator Abstract Class

Now, we define the DocumentCreator abstract class, which declares the factory method.

 1// DocumentCreator.ts
 2import { Document } from './Document';
 3
 4export abstract class DocumentCreator {
 5    abstract createDocument(): Document;
 6
 7    someOperation(): void {
 8        const document = this.createDocument();
 9        document.print();
10    }
11}

Step 4: Implement Concrete Creators

Finally, we implement the ConcreteCreators: PDFDocumentCreator and WordDocumentCreator.

 1// PDFDocumentCreator.ts
 2import { DocumentCreator } from './DocumentCreator';
 3import { PDFDocument } from './PDFDocument';
 4
 5export class PDFDocumentCreator extends DocumentCreator {
 6    createDocument(): PDFDocument {
 7        return new PDFDocument();
 8    }
 9}
10
11// WordDocumentCreator.ts
12import { DocumentCreator } from './DocumentCreator';
13import { WordDocument } from './WordDocument';
14
15export class WordDocumentCreator extends DocumentCreator {
16    createDocument(): WordDocument {
17        return new WordDocument();
18    }
19}

Step 5: Using the Factory Method Pattern

Now, let’s see how we can use these classes in our application.

 1// Main.ts
 2import { PDFDocumentCreator } from './PDFDocumentCreator';
 3import { WordDocumentCreator } from './WordDocumentCreator';
 4
 5function main() {
 6    const pdfCreator = new PDFDocumentCreator();
 7    pdfCreator.someOperation(); // Output: Printing PDF document...
 8
 9    const wordCreator = new WordDocumentCreator();
10    wordCreator.someOperation(); // Output: Printing Word document...
11}
12
13main();

Try It Yourself

Experiment with the code by adding a new document type, such as ExcelDocument, and a corresponding ExcelDocumentCreator. Observe how the Factory Method Pattern allows you to extend the system without modifying existing code.

Visualizing the Factory Method Pattern in Action

To further illustrate how the Factory Method Pattern works, let’s visualize the process flow using a sequence diagram:

    sequenceDiagram
	    participant Client
	    participant Creator
	    participant ConcreteCreator
	    participant Product
	    Client->>ConcreteCreator: createDocument()
	    ConcreteCreator->>Product: new ConcreteProduct()
	    ConcreteCreator-->>Client: ConcreteProduct
	    Client->>Product: print()

Key Benefits of the Factory Method Pattern

  • Flexibility: The pattern allows for flexibility in the types of objects created, enabling easy extension and modification of product types.
  • Loose Coupling: By decoupling the creation of objects from their usage, the pattern promotes loose coupling and enhances maintainability.
  • Adherence to the Open/Closed Principle: The pattern allows for the addition of new product types without modifying existing code, adhering to the Open/Closed Principle.

Knowledge Check

  • What is the primary intent of the Factory Method Pattern?
  • How does the Factory Method Pattern promote loose coupling?
  • What are the key participants in the Factory Method Pattern?

Summary

The Factory Method Pattern is a powerful tool in the software engineer’s toolkit, providing a flexible and scalable approach to object creation. By understanding and implementing this pattern, you can enhance the maintainability and scalability of your TypeScript applications.

Further Reading

For more information on the Factory Method Pattern, consider exploring the following resources:

Quiz Time!

Loading quiz…

Remember, this is just the beginning. As you progress, you’ll build more complex and interactive applications. Keep experimenting, stay curious, and enjoy the journey!

In this section

Revised on Thursday, April 23, 2026