Builder Pattern para Objetos de Configuracion Complejos
Usa el Builder pattern para construir objetos de configuracion complejos con parametros opcionales y valores por defecto sensatos sin constructores telescopicos
El Builder pattern separa la construccion de un objeto complejo de su representacion. En lugar de pasar ocho argumentos al constructor o crear un objeto vacio y establecer campos individualmente, el builder proporciona una API legible paso a paso con valores por defecto y validacion.
Cuando Usar Esto
- Un objeto tiene muchos parametros opcionales y valores por defecto sensatos
- Quieres prevenir que objetos se creen en un estado invalido
- El telescopio de constructores se vuelve ilegible con mas de tres argumentos opcionales
Problema
Construir una configuracion de conexion a base de datos con opciones de pooling, SSL y reintentos lleva a constructores de 12 argumentos u objetos mutables parcialmente inicializados.
Solucion
// config/DatabaseConfig.ts
interface DatabaseConfig {
host: string;
port: number;
username: string;
password: string;
database: string;
ssl?: boolean;
poolSize?: number;
maxRetries?: number;
connectionTimeout?: number;
}
class DatabaseConfigBuilder {
private config: Partial<DatabaseConfig> = {
port: 5432,
ssl: false,
poolSize: 10,
maxRetries: 3,
connectionTimeout: 5000,
};
setHost(host: string): this {
this.config.host = host;
return this;
}
setPort(port: number): this {
this.config.port = port;
return this;
}
setCredentials(username: string, password: string): this {
this.config.username = username;
this.config.password = password;
return this;
}
setDatabase(name: string): this {
this.config.database = name;
return this;
}
enableSSL(): this {
this.config.ssl = true;
return this;
}
setPoolSize(size: number): this {
this.config.poolSize = size;
return this;
}
setMaxRetries(retries: number): this {
this.config.maxRetries = retries;
return this;
}
build(): DatabaseConfig {
if (!this.config.host || !this.config.username || !this.config.database) {
throw new Error('Host, username, and database are required');
}
return this.config as DatabaseConfig;
}
}
Uso
const config = new DatabaseConfigBuilder()
.setHost('db.example.com')
.setCredentials('app_user', process.env.DB_PASSWORD!)
.setDatabase('analytics')
.enableSSL()
.setPoolSize(20)
.build();
Variaciones
- Immutable Builder: Retorna un nuevo builder en cada paso en lugar de mutar estado
- Director: Encapsula configuraciones comunes detras de una clase director
- Step Builder: Refuerza orden de construccion a traves de interfaces separadas para cada paso
Lo que funciona
- Valida solo al llamar
build(), no en cada setter; consulta Builder pattern para estrategias de validacion - Retorna
thispara encadenamiento de metodos (interfaz directa) - Congela o sella el objeto retornado para prevenir mutacion post-construccion
Errores Comunes
- Agregar logica de negocio al builder en lugar de mantenerlo como construccion pura
- Olvidar resetear estado interno cuando un builder se reutiliza
- Retornar objetos parcialmente construidos sin validacion
- Usar builders excesivamente para objetos simples con 2-3 parametros
- Mezclar logica de validacion con logica de construccion
- No documentar parametros requeridos vs opcionales
- Permitir estado mutable despues de llamar
build() - Convenciones de nombres de metodos inconsistentes
- Falta de checks de null para parametros requeridos
- No proporcionar valores por defecto sensatos para casos de uso comunes
Mejores Prácticas
-
Valida solo al llamar
build(). Valida todas las restricciones en el metodobuild()para proporcionar contexto completo de errores con todos los issues de validacion a la vez. -
Proporciona defaults sensatos. Establece valores por defecto razonables para parametros opcionales para reducir el numero de llamadas de metodo requeridas para casos de uso comunes.
-
Usa nombres de metodos descriptivos. Los nombres de metodos deberian indicar claramente que configuran (ej.
enableSSL()vssetSSL(true)). -
Documenta parametros requeridos. Distingue claramente entre pasos de configuracion requeridos y opcionales en tu documentacion y comentarios de codigo.
-
Haz el producto inmutable. Una vez que
build()retorna el objeto, no deberia ser modificable. Esto previene estado inconsistente. -
Soporta configuraciones especificas de entorno. Proporciona metodos de factory o presets para diferentes entornos (desarrollo, staging, produccion).
-
Maneja null gracefulmente. Decide si permitir valores null o lanzar excepciones, y se consistente a traves del builder.
-
Considera thread-safety. Si los builders se reusan entre threads, asegurate que sean thread-safe o no compartidos.
-
Soporta merging de configuracion. Permite que los builders mezclen configuraciones desde multiples fuentes (variables de entorno, archivos, overrides programaticos).
-
Mantén los builders enfocados. Un builder deberia construir un tipo de objeto. No añadas logica de construccion no relacionada.
Preguntas frecuentes
¿Es este patrón adecuado para proyectos pequeños?
Para proyectos pequeños con pocos componentes, este patrón puede añadir complejidad innecesaria. Empieza simple e introduce el patrón cuando sientas el problema que resuelve.
¿Cómo se compara este patrón con alternativas?
Cada patrón hace diferentes trade-offs. Revisa la tabla de variantes arriba y considera tus restricciones específicas: tamaño del equipo, requisitos de rendimiento y planes de escalado.
¿Puedo aplicar este patrón parcialmente?
Sí. Muchos equipos adoptan patrones incrementalmente. Empieza con la idea central y añade sofisticación según sea necesario. El patrón es una guía, no un blueprint estricto.
Recursos Relacionados
Patrón Abstract Factory
Crea familias de objetos relacionados sin especificar sus clases concretas. Patrón de diseño creacional para familias de objetos consistentes.
PatternProxy Pattern para Cacheo de Respuestas de API
Como implementar un proxy de cacheo que intercepta llamadas a APIs y almacena respuestas para reducir latencia y evitar peticiones redundantes
RecipeLlamar 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.