Introduction

In this tutorial, we’ll dive deep into the Factory Method Pattern, a foundational design pattern in the realm of software development, tailored specifically for TypeScript enthusiasts. The Factory 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 there is a need to encapsulate the instantiation process of a product, making a system independent of how its products are created, composed, and represented.

Read about Strategy Pattern with Java Examples

What is the Factory Method Pattern?

The Factory Method Pattern defines an interface for creating an object but lets subclasses decide which class to instantiate. It lets a class defer instantiation to subclasses, ensuring that the use of a class is separated from its instantiation.

Why Use the Factory Method Pattern?

  • Flexibility and Scalability: Easily introduce new types of products without disturbing the existing client code.
  • Decoupling: The client code is decoupled from the concrete classes, relying instead on interfaces or abstract classes.
  • Single Responsibility Principle: You can move the product creation code into one place in the application, making the code easier to support.

Real-World Analogy

Imagine you are an architect. You sketch a blueprint for a house (the product), but the actual building of houses is done by construction companies (concrete creators). Each construction company follows your blueprint but has the freedom to approach the construction with its unique techniques and materials.

Implementing the Factory Pattern in TypeScript

Scenario

In this scenario, our application needs to notify users about various events, such as new messages, system updates, or promotional offers. Users can choose their preferred notification method. Our goal is to design a flexible system that can easily accommodate new notification methods without modifying the existing codebase.

Step 1: Define the Product Interface

First, we define a Notification interface that all concrete notifications will implement. This interface will have a send method.

interface Notification {
    send(message: string): void;
}

Step 2: Create Concrete Products

Next, we implement concrete products that represent different notification methods.

class EmailNotification implements Notification {
    public send(message: string): void {
        console.log(`Sending email: ${message}`);
    }
}

class SMSNotification implements Notification {
    public send(message: string): void {
        console.log(`Sending SMS: ${message}`);
    }
}

class PushNotification implements Notification {
    public send(message: string): void {
        console.log(`Sending push notification: ${message}`);
    }
}

Step 3: Define the Creator Class

The creator class declares the factory method that is supposed to return an object of type Notification. It also includes a notifyUser method that uses the Notification object to send a message.

abstract class NotificationCreator {
    public abstract createNotification(): Notification;

    public notifyUser(message: string): void {
        const notification = this.createNotification();
        notification.send(message);
    }
}

Step 4: Implement Concrete Creators

We then implement concrete creators for each notification type. Each creator overrides the factory method to return an instance of its corresponding concrete product.

class EmailNotificationCreator extends NotificationCreator {
    public createNotification(): Notification {
        return new EmailNotification();
    }
}

class SMSNotificationCreator extends NotificationCreator {
    public createNotification(): Notification {
        return new SMSNotification();
    }
}

class PushNotificationCreator extends NotificationCreator {
    public createNotification(): Notification {
        return new PushNotification();
    }
}

Step 5: Using the Factory Method

Finally, we demonstrate how to use the factory method in our application. This example simulates sending notifications through different methods based on the creator instance.

function clientCode(creator: NotificationCreator, message: string) {
    creator.notifyUser(message);
}

console.log('User prefers email notifications:');
clientCode(new EmailNotificationCreator(), 'You have a new message!');

console.log('\nUser prefers SMS notifications:');
clientCode(new SMSNotificationCreator(), 'Your package has been delivered.');

console.log('\nUser prefers push notifications:');
clientCode(new PushNotificationCreator(), 'A new update is available for your app.');

Conclusion

This example illustrates how the Factory Method Pattern can be applied to create a flexible and scalable notification system in TypeScript. By encapsulating the creation logic within specific creator classes, our application can easily adapt to new notification methods without altering existing code. This pattern not only promotes loose coupling but also enhances the extensibility of the application.

Read more about Factory Pattern in Refactoring Guru.

Show 1 Comment

1 Comment

Comments are closed