Patron Factory
Crea objetos sin especificar la clase exacta a instanciar. Un patrón de diseño creacional para la creación flexible de objetos.
Overview
El Factory Pattern es un patrón de diseño creacional que proporciona una interfaz para crear objetos sin especificar sus clases exactas. En lugar de llamar a un constructor directamente, llamas a un método de fábrica que devuelve una nueva instancia basada en parámetros de entrada.
Este patrón es esencial cuando la lógica de creación es compleja, necesita centralizarse o debe variar en tiempo de ejecución.
When to Use
Usa el Factory Pattern cuando:
- El tipo exacto de objeto a crear se determina en tiempo de ejecución
- La creación de objetos implica configuración compleja
- Quieres desacoplar la creación del uso de objetos
- Necesitas soportar múltiples implementaciones de una interfaz
- Las pruebas requieren sustitución fácil de objetos mock
Solution
Python
from abc import ABC, abstractmethod
class Notification(ABC):
@abstractmethod
def send(self, message: str) -> str:
pass
class EmailNotification(Notification):
def send(self, message: str) -> str:
return f"Enviando email: {message}"
class SmsNotification(Notification):
def send(self, message: str) -> str:
return f"Enviando SMS: {message}"
class NotificationFactory:
@staticmethod
def create(channel: str) -> Notification:
if channel == "email":
return EmailNotification()
if channel == "sms":
return SmsNotification()
raise ValueError(f"Canal desconocido: {channel}")
# Uso
notifier = NotificationFactory.create("email")
print(notifier.send("Hola!")) # Enviando email: Hola!
JavaScript
class Notification {
send(message) {
throw new Error("No implementado");
}
}
class EmailNotification extends Notification {
send(message) {
return `Enviando email: ${message}`;
}
}
class SmsNotification extends Notification {
send(message) {
return `Enviando SMS: ${message}`;
}
}
class NotificationFactory {
static create(channel) {
switch (channel) {
case "email": return new EmailNotification();
case "sms": return new SmsNotification();
default: throw new Error(`Canal desconocido: ${channel}`);
}
}
}
const notifier = NotificationFactory.create("email");
console.log(notifier.send("Hola!")); // Enviando email: Hola!
Java
public interface Notification {
String send(String message);
}
public class EmailNotification implements Notification {
public String send(String message) {
return "Enviando email: " + message;
}
}
public class SmsNotification implements Notification {
public String send(String message) {
return "Enviando SMS: " + message;
}
}
public class NotificationFactory {
public static Notification create(String channel) {
switch (channel) {
case "email": return new EmailNotification();
case "sms": return new SmsNotification();
default: throw new IllegalArgumentException("Desconocido: " + channel);
}
}
}
// Uso
Notification notifier = NotificationFactory.create("email");
System.out.println(notifier.send("Hola!"));
Explanation
El Factory Pattern desacopla la creación de objetos de su uso a través de tres roles:
- Interfaz de Producto (
Notification): Define el contrato que todos los objetos creados deben seguir - Productos Concretos (
EmailNotification,SmsNotification): Las implementaciones reales - Fábrica (
NotificationFactory): Centraliza la lógica de creación y devuelve la instancia correcta
Esta estructura permite agregar nuevos canales de notificación sin modificar el código que usa las notificaciones.
Variants
| Variante | Caso de Uso | Compromiso |
|---|---|---|
| Simple Factory | Método único con lógica condicional | Fácil de empezar, difícil de escalar |
| Factory Method | Subclases sobreescriben la creación | Más flexible, más clases |
| Abstract Factory | Familias de objetos relacionados | Complejo, pero maneja familias de productos |
Lo que funciona
- Usa enums para canales/tipos en lugar de strings raw para evitar errores de tipeo
- Lanza errores explícitos para tipos no soportados en lugar de devolver null
- Mantén la fábrica sin estado cuando sea posible para seguridad de hilos
- Registra tipos dinámicamente en sistemas grandes (ej. contenedores de inyección de dependencias)
- Prefiere interfaces sobre herencia para el contrato del producto
Common Mistakes
- Sobre-ingeniería: Usar Abstract Factory cuando Simple Factory es suficiente
- Dispatch basado en strings: Propenso a errores de tipeo; usa enums o constantes
- Fábricas con estado: Pueden causar problemas de thread-safety en entornos multi-hilo
- Devolver null: Devolver
nullen lugar de lanzar excepciones hace más difícil rastrear bugs - Acoplamiento fuerte: Fábrica dependiendo de clases concretas en lugar de abstracciones
Troubleshooting
- Pattern does not fit the problem: re-evaluate the forces (performance, scalability, team size, coupling). A pattern is only appropriate when its trade-offs match your constraints.
- Too many abstractions: if adding a pattern increases complexity without a clear benefit, simplify. Not every module needs a factory, decorator, or strategy.
- Tight coupling after refactoring: check that interfaces are stable and dependencies point inward.
- Tests break when the design changes: favor stable contracts over internal structure.
- Performance regression from indirection: measure before and after. Layers, decorators, and adapters can add latency; cache or inline hot paths if needed.
Temas Avanzados
Escenario: Factory para Conexiones Multi-DB
// Factory pattern: crear objetos sin exponer logica de creacion
interface DatabaseConnection {
connect(): Promise<void>;
query(sql: string): Promise<unknown[]>;
close(): Promise<void>;
}
class PostgreSQLConnection implements DatabaseConnection {
constructor(private config: PGConfig) {}
async connect() { /* pg.connect() */ }
async query(sql: string) { /* pg.query(sql) */ return []; }
async close() { /* pg.end() */ }
}
class MySQLConnection implements DatabaseConnection {
constructor(private config: MySQLConfig) {}
async connect() { /* mysql.connect() */ }
async query(sql: string) { /* mysql.query(sql) */ return []; }
async close() { /* mysql.end() */ }
}
class MongoDBConnection implements DatabaseConnection {
constructor(private config: MongoConfig) {}
async connect() { /* mongo.connect() */ }
async query(sql: string) { /* mongo.find() */ return []; }
async close() { /* mongo.close() */ }
}
// Factory
class DatabaseFactory {
static create(type: "postgres" | "mysql" | "mongo", config: unknown): DatabaseConnection {
switch (type) {
case "postgres": return new PostgreSQLConnection(config as PGConfig);
case "mysql": return new MySQLConnection(config as MySQLConfig);
case "mongo": return new MongoDBConnection(config as MongoConfig);
default: throw new Error(`Unknown DB type: ${type}`);
}
}
}
// Uso: el cliente no sabe que DB se usa
const db = DatabaseFactory.create("postgres", {
host: "localhost",
port: 5432,
database: "myapp",
});
await db.connect();
const results = await db.query("SELECT * FROM users");
await db.close();
// Factory con registry (plugin pattern)
class PluginDatabaseFactory {
private static registry = new Map<string, (config: unknown) => DatabaseConnection>();
static register(type: string, creator: (config: unknown) => DatabaseConnection) {
this.registry.set(type, creator);
}
static create(type: string, config: unknown): DatabaseConnection {
const creator = this.registry.get(type);
if (!creator) throw new Error(`Unknown DB type: ${type}`);
return creator(config);
}
}
// Registrar plugins
PluginDatabaseFactory.register("postgres", (cfg) => new PostgreSQLConnection(cfg as PGConfig));
PluginDatabaseFactory.register("mysql", (cfg) => new MySQLConnection(cfg as MySQLConfig));
Lecciones:
- Factory encapsula la creacion: el cliente no conoce la clase concreta
- Anadir nuevo tipo de DB solo requiere modificar el factory
- Plugin factory permite registrar tipos sin modificar el factory
- Factory vs Abstract Factory: una clase vs familia de productos
- Factory vs DI: factory es explicito, DI es automatico
### Factory vs Abstract Factory: cual uso?
Usa Factory cuando necesitas crear un tipo de objeto con variantes (ej: DatabaseConnection con postgres/mysql/mongo). Usa Abstract Factory cuando necesitas crear una familia de objetos relacionados (ej: UI toolkit que crea Button + Input + Modal, todos del mismo tema). Factory tiene un metodo create; Abstract Factory tiene multiples metodos create para productos relacionados.
End of document. Review and update quarterly.
## Notas de Producción
- **Despliega gradualmente** usando canary o blue-green para detectar regresiones temprano.
- **Configura alertas** para errores, latencia p99 y tasa de fallos antes de habilitar en producción.
- **Documenta el rollback** en el runbook; prueba el procedimiento en staging al menos una vez por trimestre.
- **Revisa logs estructurados** con correlation IDs para trazar requests end-to-end en incidentes.
## Puntos Clave
- **Aplica factory pattern** cuando necesites una solución práctica para tu caso de uso.
- **Monitorea el rendimiento** después de implementar; mide latencia, errores y uso de recursos antes y después.
- **Revisa la sección de Troubleshooting** ante errores comunes; la mayoría tienen causa raíz documentada con solución.
- **Mantén dependencias actualizadas** y ejecuta tests en CI para prevenir regresiones en producción.
## Errores Comunes en Producción
- Aplicar el patrón donde no se necesita abstracción, agregando complejidad accidental.
- Dejar que el patrón se filtre en módulos no relacionados y confundir los límites de responsabilidad.
- Sobre-ingeniería en la primera implementación en lugar de comenzar simple y medir el dolor.
- Saltar los tests de contrato, de modo que las refactorizaciones rompan consumidores en silencio.
- Ignorar modos de fallo que el patrón no cubre.
- Usar el patrón como opción por defecto en lugar de elegir la herramienta adecuada para la escala actual.
- Olvidar documentar cuándo dejar de usar el patrón y qué lo reemplaza.
- Carecer de observabilidad sobre rendimiento y propagación de errores del patrón. Preguntas frecuentes
¿Cuál es la diferencia entre Factory Method y Abstract Factory?
Factory Method permite que las subclases decidan qué clase instanciar. Abstract Factory crea familias de objetos relacionados (ej. componentes UI para Windows vs. Mac).
¿Es el Factory Pattern lo mismo que la inyección de dependencias?
No. DI se trata de quién provee la dependencia; Factory se trata de cómo se crea la dependencia. A menudo funcionan juntos.
¿Cuándo debería evitar el Factory Pattern?
Evítalo cuando la creación de objetos es trivial (un simple new Class()) y hay solo una implementación.
Recursos Relacionados
Llamar a una API REST: Python, JS, Java y Go
Cómo hacer peticiones HTTP a una API REST y manejar la respuesta JSON en Python, JavaScript, Java y Go.
RecipeParsear JSON
Cómo parsear cadenas JSON a estructuras de datos nativas en varios lenguajes de programación.
GuideGuía de Diseño de APIs REST
Una Referencia Detallada para diseñar APIs REST limpias, escalables y mantenibles.
PatternPatrón Abstract Factory
Crea familias de objetos relacionados sin especificar sus clases concretas. Patrón de diseño creacional para familias de objetos consistentes.
PatternPatrón Builder
Construye objetos complejos paso a paso. Patrón de diseño creacional para construcción de objetos legible y configurable.
PatternPatrón Bridge: Desacopla Abstracción e Implementación
Dividí una clase en dos jerarquías — abstracción e implementación — para que ambas evolucionen independientemente. Con ejemplos en Python, Java y JavaScript.