-
Notifications
You must be signed in to change notification settings - Fork 1
FactoryPattern
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 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.
AGENDA :
-
Define a superclass/interface with a factory method that returns instances of subclasses.
// Superclass: PaymentGateway interface PaymentGateway { processPayment(amount: number): void; }
-
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`); } }
-
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:
- 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.
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).
-
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; }
-
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`); } }
-
Define an abstract factory interface that declares methods for creating products.
// Abstract Factory: PaymentFactory interface PaymentFactory { createPaymentGateway(): PaymentGateway; createPaymentProcessor(): PaymentProcessor; }
-
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(); } }
- Clients use the Abstract Factory interface to create families of related products (payment gateways and processors) without specifying their concrete classes.
- Concrete factory classes produce concrete products that belong to the same family.
- Clients work with products through their common interfaces, ensuring compatibility within the created families.
- 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.