Today I discussed design patterns, specifically factory design patterns. There are three main types of factory patterns:
Simple Factory is used when we have an input parameter and need to create a specific class or instance based on that parameters.
It's straightforward and commonly used in everyday programming.
Here's a simple example:
// Product interface
interface Animal {
makeSound(): void;
}
// Concrete products
class Dog implements Animal {
makeSound() {
console.log("Woof!");
}
}
class Cat implements Animal {
makeSound() {
console.log("Meow!");
}
}
// Simple factory
class AnimalFactory {
createAnimal(type: string): Animal {
switch(type.toLowerCase()) {
case "dog":
return new Dog();
case "cat":
return new Cat();
default:
throw new Error("Unknown animal type");
}
}
}
// Usage
const factory = new AnimalFactory();
const dog = factory.createAnimal("dog");
const cat = factory.createAnimal("cat");
dog.makeSound(); // Output: Woof!
cat.makeSound(); // Output: Meow!
In this example, the AnimalFactory is a simple factory that creates different types of animals based on a string parameter. It demonstrates how a simple factory can encapsulate object creation logic in a single place.
IMPROTANT:
Dynamic Creation Discussion
Sometimes, you need to inject additional logic in your factory method to determine which variant of a product to return. For instance, you might decide between a “US” version or “EU” version of the same service based on user location, configuration, or runtime parameters. This does not violate the Factory Method pattern as long as:
Consider the following dynamic example, where the same factory creates slightly different variants of a payment service depending on the region:
// Product interface
interface PaymentService {
pay(amount: number): void;
}
// Concrete products
class PayPalUSService implements PaymentService {
pay(amount: number): void {
console.log(`PayPal US processing payment: $${amount}`);
}
}
class PayPalEUService implements PaymentService {
pay(amount: number): void {
console.log(`PayPal EU processing payment: €${amount}`);
}
}
// Creator interface
interface PaymentFactory {
createPaymentService(): PaymentService;
}
// Concrete creator with dynamic logic
class PayPalFactory implements PaymentFactory {
constructor(private region: string) {}
createPaymentService(): PaymentService {
// Decide which PayPal variant to create, all still PaymentService
if (this.region.toLowerCase() === "eu") {
return new PayPalEUService();
}
return new PayPalUSService();
}
}
// Usage
const euFactory = new PayPalFactory("EU");
const euPayment = euFactory.createPaymentService();
euPayment.pay(100); // Output: PayPal EU processing payment: €100
const usFactory = new PayPalFactory("US");
const usPayment = usFactory.createPaymentService();
usPayment.pay(100); // Output: PayPal US processing payment: $100