Back

Creación Flexible de Objetos con el Patrón Builder en JavaScript

Creación Flexible de Objetos con el Patrón Builder en JavaScript

Tienes una función que crea objetos de usuario. Comienza con tres parámetros, luego crece a cinco, después a siete. La mitad de ellos son opcionales. Quienes la llaman deben recordar el orden exacto, y una posición incorrecta produce silenciosamente un objeto defectuoso. Este es el problema que resuelve el Patrón Builder en JavaScript.

Puntos Clave

  • El Patrón Builder construye objetos paso a paso utilizando métodos setter encadenados y una llamada final a build(), reemplazando las listas de argumentos posicionales propensas a errores.
  • Una API fluida, donde cada setter retorna this, hace que el sitio de llamada sea autodocumentado e independiente del orden.
  • Reserva el patrón para objetos con muchos parámetros opcionales, validación de campos requeridos o reglas de validación. Para casos más simples, utiliza objetos literales o funciones factory.
  • El método build() es el único lugar para validar campos requeridos y retornar un objeto limpio y congelado, separado del builder mismo.

¿Qué es el Patrón Builder en JavaScript?

El Patrón Builder es un patrón de diseño creacional que construye objetos paso a paso en lugar de todo a la vez. En lugar de pasar cada valor a una única llamada de constructor, encadenas métodos setter y finalizas la creación con un paso build() que valida y retorna el objeto completado.

No es una solución universal. Para objetos simples con dos o tres campos bien definidos, un objeto literal plano o una función factory es más limpio. El Patrón Builder gana su lugar cuando la creación de objetos implica:

  • Muchos parámetros opcionales donde el orden no importa
  • Reglas de validación que deben ejecutarse antes de que el objeto sea utilizado
  • Campos requeridos que necesitan ser validados en el momento de la creación
  • Construcción en múltiples pasos donde los estados intermedios no deben ser expuestos

El Problema: Contaminación del Constructor

Considera este patrón común:

// ❌ Difícil de leer, fácil confundir el orden de los argumentos
const request = new ApiRequest('GET', '/users', null, true, 5000, 'json')

Seis argumentos posicionales. Sin etiquetas. Sin validación. Si intercambias dos valores, nada te advierte.

Un Ejemplo Limpio del Patrón Builder en JavaScript

Aquí hay una implementación basada en clases utilizando una API fluida en JavaScript—donde cada setter retorna this, habilitando el encadenamiento de métodos:

class ApiRequestBuilder {
  constructor() {
    this.method = 'GET'        // sensible default
    this.url = null
    this.body = null
    this.timeout = 3000        // default timeout
    this.responseType = 'json'
  }

  setMethod(method) {
    this.method = method
    return this
  }

  setUrl(url) {
    this.url = url
    return this
  }

  setBody(body) {
    this.body = body
    return this
  }

  setTimeout(ms) {
    this.timeout = ms
    return this
  }

  build() {
    if (!this.url) {
      throw new Error('URL is required')
    }
    // Return a plain, frozen object—not the builder itself
    return Object.freeze({
      method: this.method,
      url: this.url,
      body: this.body,
      timeout: this.timeout,
      responseType: this.responseType,
    })
  }
}

// Usage
const request = new ApiRequestBuilder()
  .setUrl('/api/users')
  .setMethod('POST')
  .setBody({ name: 'Alice' })
  .build()

Cada llamada es autodocumentada. La validación se ejecuta en build() antes de que el objeto sea utilizado. Los valores predeterminados se aplican automáticamente. Observa que build() retorna un objeto plano congelado—no el builder—lo que mantiene el resultado limpio y previene mutaciones accidentales.

Builder vs. Alternativas Más Simples

EscenarioMejor Enfoque
2–3 campos requeridos, sin validaciónObjeto literal o función factory
Campos opcionales, sin reglasParámetros nombrados mediante createUser({ name, age })
Campos requeridos + validación + valores predeterminadosPatrón Builder
Construcción compleja en múltiples pasosPatrón Builder

Una función factory con parámetros nombrados como createRequest({ url, method = 'GET' }) maneja muchos casos de forma limpia. Recurre a un builder cuando la lógica de validación o la secuenciación hagan que esa función sea difícil de razonar.

Una Nota sobre TypeScript

TypeScript puede hacer que los builders sean significativamente más seguros. Puedes forzar que build() solo sea invocable después de que los setters requeridos hayan sido llamados, utilizando tipos condicionales o una interfaz step-builder. Si tu proyecto usa TypeScript, vale la pena explorarlo—pero el patrón JavaScript básico funciona bien sin él.

Conclusión

Utiliza el Patrón Builder cuando la creación de objetos tenga reglas que necesiten ser aplicadas, no solo como estrategia predeterminada de creación de objetos. La API fluida hace que el sitio de llamada sea legible, el paso build() hace que la validación sea explícita, y los valores predeterminados reducen el ruido. Para todo lo más simple, una función factory o un objeto literal plano es la herramienta correcta.

Preguntas Frecuentes

Un objeto de opciones agrupa parámetros nombrados, lo que resuelve el problema de los argumentos posicionales. Un builder añade un paso de construcción donde puedes validar campos requeridos, aplicar restricciones y congelar el resultado antes de que sea utilizado. Si necesitas esas garantías, un builder es la mejor opción. Si solo necesitas claves nombradas con valores predeterminados, un objeto de opciones es más simple.

Sí, pero ten cuidado. Después de llamar a build, el builder aún mantiene el estado de la configuración anterior. Debes resetear cada campo o crear una nueva instancia de builder para cada objeto. Crear una nueva instancia cada vez es el enfoque más seguro y predecible.

Puede hacerlo. Si tu objeto tiene solo unos pocos campos bien conocidos y sin reglas de validación, un objeto literal plano o una función factory con parámetros nombrados es más limpio. El Patrón Builder vale la pena cuando el número de campos opcionales crece, cuando los valores predeterminados interactúan, o cuando la creación requiere restricciones aplicadas.

Object.freeze previene que las propiedades de nivel superior del objeto retornado sean modificadas después de la creación. Esto mantiene el resultado construido predecible y de solo lectura, lo cual es especialmente útil cuando el objeto es pasado a través de múltiples capas de código. Establece un límite claro entre el tiempo de configuración dentro del builder y el tiempo de uso fuera de él.

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay