domingo, 19 de marzo de 2017

Patrones de diseño (Builder Pattern) - ¿Qué hacer cuando se tiene demasiados parámetros en el constructor?

El patrón de construcción (Builder Pattern) es un patrón creacional que se utiliza cuando se tiene demasiados parámetros en un constructor. De acuerdo a Robert C. Martin en su libro Clean Code (lectura sumamente aconsejable), el número ideal de argumentos que se debe enviar a una función es cero, luego un parámetro, luego dos y luego tres. En caso de necesitar una mayor cantidad de parámetros se debe buscar una muy buena justificación para utilizarlos y aún así no utilizarlos :D

Entonces, ¿qué hacemos cuando necesitamos construir una Objeto con bastantes parámetros y se necesita mucha lógica de construcción? Builder Pattern al rescate!!!

Primero veremos un ejemplo de la construcción de una persona de la forma convencional:

function Persona(nombre, apellidos, edad,
                 sexo, altura, tonoDePiel, tipoDeCabello) {
  this.nombre = nombre;
  this.apellidos = apellidos;
  this.edad = edad;
  this.sexo = sexo;
  this.altura = altura;
  this.tonoDePiel = tonoDePiel;
  this.tipoDeCabello = tipoDeCabello;
}

var persona1 = new Persona('Adrian', 'Espinoza', 32,
                            'Masculino', 1.80,
                            '#f3f312', 'lacio');
var persona2 = new Persona('Magda', 'Apaza', 12,
                            'Femenino', 1.60,
                            '#13f3f2', 'lacio');

console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona1);
console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona2);
console.log('+++++++++++++++++++++++++++++++++++++++++');

¿Funciona? Claro que funciona, pero... ¿qué pasaría si quiero agregar un atributo? tendría que modificar tanto Persona como sus creaciones, y ¿si modifico el orden de los atributos? :O se vueve cada vez más complicado el mantenimiento. Ahora veamos el mismo ejemplo con el Builder Pattern:

// Persona builder
function PersonaBuilder() {
  // atributos del builder
  var nombre;
  var apellidos;
  var edad;
  var sexo;
  var altura;
  var tonoDePiel;
  var tipoDeCabello;

  // Objeto persona
  function Persona(personaBuilder) {
    this.nombre = personaBuilder.getNombre();
    this.apellidos = personaBuilder.getApellidos();
    this.edad = personaBuilder.getEdad();
    this.sexo = personaBuilder.getSexo();
    this.altura = personaBuilder.getAltura();
    this.tonoDePiel = personaBuilder.getTonoDePiel();
    this.tipoDeCabello = personaBuilder.getTipoDeCabello();
  }

  // getters, setters y builder
  return {
    setNombre: function (nombre) {
      this.nombre = nombre;
      return this;
    },
    getNombre: function() {
      return this.nombre;
    },
    setApellidos: function (apellidos) {
      this.apellidos = apellidos;
      return this;
    },
    getApellidos: function() {
      return this.apellidos;
    },
    setEdad: function (edad) {
      this.edad = edad;
      return this;
    },
    getEdad: function() {
      return this.edad;
    },
    setSexo: function (sexo) {
      this.sexo = sexo;
      return this;
    },
    getSexo: function() {
      return this.sexo;
    },
    setAltura: function (altura) {
      this.altura = altura;
      return this;
    },
    getAltura: function() {
      return this.altura;
    },
    setTonoDePiel: function (tonoDePiel) {
      this.tonoDePiel = tonoDePiel;
      return this;
    },
    getTonoDePiel: function() {
      return this.tonoDePiel;
    },
    setTipoDeCabello: function (tipoDeCabello) {
      this.tipoDeCabello = tipoDeCabello;
      return this;
    },
    getTipoDeCabello: function() {
      return this.tipoDeCabello;
    },
    build: function () {
      return new Persona(this);
    }
  };
}

// Ahora crear personas ss mucho más simple, además el ordén de los 
// atributos no importa acá
var personaBuilder = new PersonaBuilder();
var persona1 = personaBuilder.setNombre('Jhon')
                            .setApellidos('Perez Apaza')
                            .setEdad(25)
                            .setAltura(1.75)
                            .setSexo('Masculino')
                            .setTonoDePiel('#fa45fc')
                            .setTipoDeCabello('lacio')
                            .build();

var persona2 = personaBuilder.setNombre('Jackie')
                              .setApellidos('Sullivan')
                              .setTipoDeCabello('lacio')
                              .setSexo('Femenino')
                              .setEdad(45)
                              .setAltura(1.50)
                              .setTonoDePiel('#aaa53c')
                              .build();

console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona1);
console.log('+++++++++++++++++++++++++++++++++++++++++');
console.log(persona2);
console.log('+++++++++++++++++++++++++++++++++++++++++');


Ahora es mucho más sencillo realizar cambios y mantenerlos controlados, además, el ordén en que se agregan los atributos ya no importa.

Salida del script anterior

Y eso es todo por ahora. Cabe destacar que no es necesario utilizar cada patrón en todas las ocasiones que se presentan, ya que los patrones agregan complejidad que no siempre es necesario en un proyecto. Este ejemplo puede utilizarse en un proyecto donde sea necesario crear muchas personas y con muchos atributos, pero esto no sucede en todo slos proyectos que estamos utilizando. Para evitar crear un ibjeto con demasiados parámetros también se pueden ir agrupando los parámetros en objetos pequeños para luego construir el objeto completo.

Y eso es todo, recuerden, KISS (Keep It Simple S...) y Keep Coding XD

No hay comentarios:

Publicar un comentario