Skip to content

FactoryPattern

MidasXIV edited this page May 2, 2024 · 4 revisions

Factory Pattern

The Factory Pattern is a creational design pattern that means it's a type of design pattern that deals with the creation of objects. It helps to decouple the creation logic of classes to the class that decides which class to be instantiate. It has one popular variant called the Abstract Factory Pattern which similar to the factory pattern also helps in creating objects, it provides a way to create families of related or dependent objects, It allows you to create objects without specifying the concrete classes.

A common description of the factory pattern is "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."

What this means is The abstract Factory class has methods (provides an interface) that need need to be implemented via the classes that implement it (called subclasses), but the objects of those classes are different, thereby altering the type of objects that will be created. In general, both patterns provide a way to decouple the creation of objects from the specific classes of objects. The Factory Pattern is useful when you need to create objects of different classes, while the Abstract Factory Pattern is useful when you need to create families of related objects.

In essence, the Factory pattern is used to loosen the coupling between code that decides which object should be created from the code that does the object creation. It's a pattern that helps you create objects in a way that's flexible, reusable, and maintainable.

When to Think of the Factory Pattern:

  • When you need to encapsulate object creation logic for related products.
  • When you want to provide a common interface for creating different types of objects.

The easiest way to understand the intent of the Factory pattern is to look at examples of where it is useful or is used.

Imagine we are building a SaaS and now we have to support different payment gateways such as PayPal, Stripe, and Square. Each payment gateway has its own implementation for processing payments. To implement this we'll use a PaymentGatewayFactory to create instances of these payment gateways based on the selected type.


Steps to implementing the said pattern in typescript.

AGENDA :

  1. Define a superclass/interface with a factory method that returns instances of subclasses.

    // Superclass: PaymentGateway
    interface PaymentGateway {
        processPayment(amount: number): void;
    }
  2. Implement concrete subclasses that implement the superclass/interface.

    // Concrete subclass: PayPal
    class PayPal implements PaymentGateway {
        processPayment(amount: number) {
            console.log(`Processing payment of $${amount} via PayPal`);
        }
    }
    
    // Concrete subclass: Stripe
    class Stripe implements PaymentGateway {
        processPayment(amount: number) {
            console.log(`Processing payment of $${amount} via Stripe`);
        }
    }
    
    // Concrete subclass: Square
    class Square implements PaymentGateway {
        processPayment(amount: number) {
            console.log(`Processing payment of $${amount} via Square`);
        }
    }
  3. Create a factory class that encapsulates the object creation logic and returns instances of subclasses based on certain conditions or parameters.

    // Factory class: PaymentGatewayFactory
    class PaymentGatewayFactory {
        createPaymentGateway(type: string): PaymentGateway {
            switch (type.toLowerCase()) {
                case "paypal":
                    return new PayPal();
                case "stripe":
                    return new Stripe();
                case "square":
                    return new Square();
                default:
                    throw new Error("Invalid payment gateway type");
            }
        }
    }

Now, let's move on to the Abstract Factory pattern for a Payment Gateway:

Abstract Factory Pattern Example: Payment Gateway

When to Think of the Abstract Factory Pattern:

  • When you need to create families of related or dependent objects.
  • When you want to ensure that the created objects are compatible with each other.

Example of Usage:

In our scenario, we not only need to create different payment gateways, but we also need to create corresponding payment processors for each gateway. We'll use an Abstract Factory to create families of related products (payment gateways and processors).

Steps to Implementing the Abstract Factory Pattern in TypeScript:

  1. Define interfaces for the products (PaymentGateway and PaymentProcessor).

    // Abstract Product: PaymentGateway
    interface PaymentGateway {
        processPayment(amount: number): void;
    }
    
    // Abstract Product: PaymentProcessor
    interface PaymentProcessor {
        processPayment(amount: number): void;
    }
  2. Implement concrete product classes for each product interface.

    // Concrete Product: PayPal
    class PayPal implements PaymentGateway, PaymentProcessor {
        processPayment(amount: number) {
            console.log(`Processing payment of $${amount} via PayPal`);
        }
    }
    
    // Concrete Product: Stripe
    class Stripe implements PaymentGateway, PaymentProcessor {
        processPayment(amount: number) {
            console.log(`Processing payment of $${amount} via Stripe`);
        }
    }
    
    // Concrete Product: Square
    class Square implements PaymentGateway, PaymentProcessor {
        processPayment(amount: number) {
            console.log(`Processing payment of $${amount} via Square`);
        }
    }
  3. Define an abstract factory interface that declares methods for creating products.

    // Abstract Factory: PaymentFactory
    interface PaymentFactory {
        createPaymentGateway(): PaymentGateway;
        createPaymentProcessor(): PaymentProcessor;
    }
  4. Implement concrete factory classes that produce families of related products.

    // Concrete Factory: PayPalFactory
    class PayPalFactory implements PaymentFactory {
        createPaymentGateway(): PaymentGateway {
            return new PayPal();
        }
    
        createPaymentProcessor(): PaymentProcessor {
            return new PayPal();
        }
    }
    
    // Concrete Factory: StripeFactory
    class StripeFactory implements PaymentFactory {
        createPaymentGateway(): PaymentGateway {
            return new Stripe();
        }
    
        createPaymentProcessor(): PaymentProcessor {
            return new Stripe();
        }
    }
    
    // Concrete Factory: SquareFactory
    class SquareFactory implements PaymentFactory {
        createPaymentGateway(): PaymentGateway {
            return new Square();
        }
    
        createPaymentProcessor(): PaymentProcessor {
            return new Square();
        }
    }

How it Works:

  1. Clients use the Abstract Factory interface to create families of related products (payment gateways and processors) without specifying their concrete classes.
  2. Concrete factory classes produce concrete products that belong to the same family.
  3. Clients work with products through their common interfaces, ensuring compatibility within the created families.

Variants

Abstract Factory Pattern

When to Use the Factory and Abstract Factory Patterns Over Other Patterns:

  • Use the Factory pattern when you need to encapsulate object creation logic and delegate the responsibility of object instantiation to subclasses.
  • Use the Abstract Factory pattern when you need to create families of related or dependent objects and ensure their compatibility.

Clone this wiki locally