Patrón Factory Method: Delegando la Creación de Objetos

¿Qué es el Patrón Factory Method?
El patrón Factory Method define una interfaz para crear objetos, pero permite a las subclases decidir qué clase instanciar. Es ideal cuando no conoces de antemano los tipos exactos de objetos que necesitas crear o cuando quieres delegar la lógica de creación a subclases especializadas.
classDiagram
class Creator {
<<abstract>>
+factoryMethod(): Product
+operation()
}
class ConcreteCreatorA {
+factoryMethod(): ProductA
}
class ConcreteCreatorB {
+factoryMethod(): ProductB
}
class Product {
<<interface>>
+use()
}
class ProductA {
+use()
}
class ProductB {
+use()
}
Creator <|-- ConcreteCreatorA
Creator <|-- ConcreteCreatorB
Product <|.. ProductA
Product <|.. ProductB
Creator ..> ProductCuándo Usar Factory Method
Usar Factory Method:
- Cuando no conoces de antemano los tipos exactos de objetos
- Para proporcionar una forma de extender los componentes internos
- Para reutilizar objetos existentes en lugar de crear nuevos
- Integración con diferentes proveedores (pagos, notificaciones, etc.)
Evitar Factory Method:
- Si solo tienes un tipo de producto (sobreingeniería)
- Cuando la creación es trivial y no cambiará
Implementación Paso a Paso
1. Interfaz del Producto
Define el contrato común que todos los productos deben implementar:
interface NotificationResult {
success: boolean;
messageId: string;
timestamp: Date;
}
interface Notification {
readonly type: string;
send(recipient: string, message: string): Promise<NotificationResult>;
validate(recipient: string): boolean;
}2. Productos Concretos
Implementaciones específicas de la interfaz:
class EmailNotification implements Notification {
readonly type = 'email';
validate(recipient: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(recipient);
}
async send(recipient: string, message: string): Promise<NotificationResult> {
console.log(`Sending email to ${recipient}: ${message}`);
return {
success: true,
messageId: `email_${Date.now()}`,
timestamp: new Date(),
};
}
}
class SMSNotification implements Notification {
readonly type = 'sms';
validate(recipient: string): boolean {
const phoneRegex = /^\+?[1-9]\d{9,14}$/;
return phoneRegex.test(recipient);
}
async send(recipient: string, message: string): Promise<NotificationResult> {
console.log(`Sending SMS to ${recipient}: ${message}`);
return {
success: true,
messageId: `sms_${Date.now()}`,
timestamp: new Date(),
};
}
}3. Creador Abstracto
Define el factory method abstracto y puede incluir lógica común:
abstract class NotificationCreator {
/** Factory Method - las subclases deciden qué crear */
protected abstract createNotification(): Notification;
/** Template Method que usa el factory method */
public async notify(
recipient: string,
message: string
): Promise<NotificationResult> {
const notification = this.createNotification();
if (!notification.validate(recipient)) {
throw new Error(`Destinatario inválido para ${notification.type}`);
}
return notification.send(recipient, message);
}
}4. Creadores Concretos
Implementan el factory method para crear productos específicos:
class EmailNotificationCreator extends NotificationCreator {
protected createNotification(): Notification {
return new EmailNotification();
}
}
class SMSNotificationCreator extends NotificationCreator {
protected createNotification(): Notification {
return new SMSNotification();
}
}Ejemplo de Uso
// Usando creadores específicos
const emailCreator = new EmailNotificationCreator();
await emailCreator.notify('[email protected]', '¡Bienvenido!');
const smsCreator = new SMSNotificationCreator();
await smsCreator.notify('+34612345678', 'Tu código es: 123456');Variante: Factory con Registro Dinámico
Permite registrar nuevos tipos en tiempo de ejecución:
type NotificationType = 'email' | 'sms' | 'push' | 'slack';
class NotificationFactory {
private static creators = new Map<NotificationType, () => Notification>();
static register(type: NotificationType, creator: () => Notification): void {
this.creators.set(type, creator);
}
static create(type: NotificationType): Notification {
const creator = this.creators.get(type);
if (!creator) {
throw new Error(`Tipo de notificación no registrado: ${type}`);
}
return creator();
}
}
// Registro de factories
NotificationFactory.register('email', () => new EmailNotification());
NotificationFactory.register('sms', () => new SMSNotification());
// Uso
const notification = NotificationFactory.create('email');
await notification.send('[email protected]', 'Hello!');Resumen
| Aspecto | Descripción |
|---|---|
| Propósito | Delegar la creación de objetos a subclases |
| Componentes | Producto (interfaz), Productos concretos, Creador abstracto, Creadores concretos |
| Beneficios | Desacoplamiento, extensibilidad, principio Open/Closed |
| Variantes | Factory con registro dinámico, Parametrized Factory |
Conclusión
El patrón Factory Method es una herramienta esencial para manejar la creación de objetos de forma flexible y extensible. Es la base de muchos frameworks y librerías modernas.
En el próximo artículo exploraremos el Patrón Decorator, que permite añadir comportamiento a objetos dinámicamente.
Basado en "Design Patterns: Elements of Reusable Object-Oriented Software" (Gang of Four).