JavaScript — Patrones de diseño en JS — Creacional (Parte II)

Mauricio Garcia
10 min readJun 2, 2020

--

Temario

  • Object — Abstract Factory
  • Object — Builder (Constructor)
  • Object — Prototype
  • Object — Singleton

i. Creational — Object — Abstract Factory

Este patrón se le considera como super-factory, ya que de manera interna tiene dos o más Factory Method

Imaginemos lo siguiente:

En la imagen anterior, podemos observar que tenemos una clase principal Vehicle y que tenemos 8 subclases (4 para carros, 4 para motos), para llegar al patrón de Abstract Factory, primero debemos usar el Factory Method, entonces hagamos el cambio:

Lo que hacemos es crear un Factory para cada uno de los tipos MotorcycleFactory y CarFactory, donde cada uno de ellos va a hacer la instancia del objeto dependiendo de las propiedades que se le pasen; hasta aquí hemos cumplido con el patrón de Factory Method, ahora lo que vamos a hacer es crear un super-factory FactoryProvider, para que esté a su vez mande a llamar los factory MotorcycleFactory, CarFactory.

Hagamos los ajustes:

Ahora veamos como queda a nivel código:

Primero generamos la clase principal Vehicle, donde va a tener sus propiedades y métodos:

Realizamos una subclase para cada uno de los tipos:

Generamos dos clases Factory MotorcycleFactory y CarFactory, donde dependiendo del modelo model que se la mande es la instancia de objeto que va a realizar:

Y por último creamos el super-factory que es la clase FactoryProvider, donde va a mandar a llamar a las clases Factory, dependiendo del tipo type que se le mande:

Veamos paso a paso cómo funciona:

Primero hacemos la instancia del super-factory:

Después mandamos a llamar el método createType donde internamente hace la instancia de la clase Factory dependiendo el tipo type:

Y por último mandamos a llamar el método create donde internamente hace la instancia a la subclase dependiendo el modelo model:

— Ejemplo completo

Se recomienda usar cuando…

  • El cliente es independiente a cómo creamos los objetos.
  • Detectamos que hay más de dos grupos del patrón de Factory method.
  • No se quiere depender de las subclases o son desconocidos para el cliente (tener mayor flexibilidad).

La ventaja de usarlo…

  • Una aplicación bien diseñada, cada clase es responsable de una sola cosa.
  • Cumple con el principio de SOLID (Principio de responsabilidad única).
  • Se pueden desarrollar interfaces más pequeñas o segmentadas.
  • Evita el acoplamiento cerrado entre los productos.
  • Cumple con el principio de SOLID (Abierto / cerrado), ya que podemos meter nuevos tipos sin romper el código existente.

La desventaja de usarlo…

  • A pesar de que el patrón es bastante flexible, puede generar problemas al momento de crear más instancias, ya que conforme vamos agregando nuevos tipos, es necesario ir haciendo ajustes a las subclases
  • El mantenimiento del código puede volverse muy complicado ya que se introducen muchas nuevas interfaces y clases junto con el patrón.

El patrón de Abstract Factory se basa en un conjunto de patrones de Factory Method, pero también puede usar Prototype para componer los métodos en estas clases.

ii. Creational — Object — Builder (Constructor)

¿Has observado que en la mayoría de los ejercicios que hacemos hemos agregado el método constructor?, Este método es el encargado de inicializar al objeto instanciado con un conjunto de propiedades que se le han proporcionado.

Con este patrón podemos crear un objeto desde un principio (mandando las propiedades en el constructor).

:: Ejemplo

Veamos la siguiente imagen:

La idea es crear un carro a partir de diferentes propiedades (tipo de carro, motor, asientos, color, etc…).

Primero vamos a crear la clase Vehicle, donde va a recibir por medio del constructor las propiedades model, type, color

Tenemos una clase Vehicle, cuando se hace la instancia de objeto espera los valores :

— Ejemplo completo

Se recomienda usar cuando…

  • El número de parámetros a pasar por el constructor es pequeño y fijo.
  • Cuando necesitamos inicializar la instancia de un objeto con propiedades específicas.

La ventaja de usarlo…

  • Se hace una interfaz simple y sencilla de usar.
  • Cumple con el principio de SOLID (Principio de responsabilidad única).

La desventaja de usarlo…

  • Si hay cambios en el constructor, impactan los cambios al cliente.

:: Ejemplo 2.1

Hasta aquí hemos visto un ejemplo sencillo, la idea es mejorar un poco el patrón

Podemos usar este patrón de forma básica siempre y cuando nuestros parámetros de inicialización (cuando construimos nuestro objeto) sean pocos o fijos, ya que podemos llegar a saturar el constructor con muchos parámetros, veamos la siguiente imagen:

Podemos observar una gran variedad de tipos de carros, desde el color, hasta el tipo de máquina que tiene, imaginemos que necesitamos armar un carro con las siguientes propiedades:

Si pasamos por parámetros todas esas propiedades, quedaría de la siguiente manera:

La clase Vehicle, quedaría de la siguiente manera:

Nuestra subclase Car:

Nota: Para evitar mucho código, en este ejercicio he decidido no implementar los métodos de acceso get y set.

Seamos honestos, muy pocos carros tienen aleta trasera, escape modificado, inclusive turbo o ya ni se diga quemacocos; entonces al hacer la instancia de objeto de cada uno de ellos, vamos a generar muchas propiedades null o undefined.

Estoy seguro que la solución que piensas es: “crear subclases por tipo”, si…de hecho no es mala ideapero el problema es que se generarían muchas subclases y para este patrón no es buena idea, por lo que el patrón Builder nos sugiere que hagamos sub-constructores (creación de un objeto paso a paso) dentro de la clase, para que estas solo se llamen cuando se ocupen; adaptemos el ejemplo anterior:

En el constructor vamos a dejar las propiedades que tienen en común TODOS los carros, y vamos a crear un método (constructor) por cada una de las otras propiedades:

Con esto nos da la libertad de generar el carro que nosotros necesitemos, sin necesidad de tener propiedades de tipo null o undefined.

Para instanciarlo:

Cuando queremos un carro de lujo:

Cuando queremos un carro de carreras:

Cuando queremos un carro común:

En cada uno de los ejemplos, estamos construyendo un carro de acuerdo a nuestras necesidades.

— Ejemplo completo

Se recomienda usar cuando…

  • El número de parámetros a pasar por el constructor es grande o variable.
  • Cuando no sabemos exactamente cuántas propiedades vamos a utilizar.

La ventaja de usarlo…

  • Cumple con el principio de SOLID (Principio de responsabilidad única).
  • Cumple con el principio de SOLID (Abierto / cerrado), ya que podemos meter nuevos tipos sin romper el código existente.
  • Se puede construir objetos paso a paso, dando la libertad de agregar u omitir pasos.
  • Puede reutilizar el mismo código de construcción al crear varias representaciones de productos.

La desventaja de usarlo…

  • La complejidad general del código aumenta ya que el patrón requiere la creación de múltiples constructores, ya que por cada propiedad agregada, se necesita construir su propio constructor.

:: Ejemplo 2.2

Podríamos ir un poco más allá y crear una clase maestra MasterClass para que nos genere instancia de objetos de acuerdo a un criterio, al realizar esto, al usuario le damos la oportunidad de hacer instancias más limpias, veamos el ejemplo:

Hacemos un pequeño cambio en la clase Car :

Ahora generamos una clase maestra MasterClass, donde vamos crear métodos predeterminados para armar tipos de carros específicos:

Ahora hacemos la instancia de objeto de nuestros carros:

Si observamos el ejemplo anterior, podemos crear carros predeterminados con la clase Master o seguir generando carros custom.

— Ejemplo completo

Se recomienda usar cuando…

  • Se pueden generar grupos de propiedades.

La ventaja de usarlo…

  • Cumple con el principio de SOLID (Principio de responsabilidad única).
  • Cumple con el principio de SOLID (Abierto / cerrado), ya que podemos meter nuevos tipos sin romper el código existente.
  • La construcción del lado del cliente es más sencilla.

La desventaja de usarlo…

  • La complejidad general del código aumenta ya que requiere la creación de una clase extra.

iii. Creational — Object — Prototype

Este patrón nos sirve para crear nuevos objetos a partir de un objeto ya existente y evitar hacer una nueva instancia desde cero.

:: Ejemplo 1

Imagina que tenemos la instancia de un carro, donde ya tiene propiedades muy específicas, y surge la necesidad de tener otra instancia exactamente igual, entonces, para no generar una desde cero, la idea es “clonar” la existente y trabajar con ella de manera separada.

Vamos a crear la clase Vehicle, donde va a tener un método llamado clone, que va a ser la encargada de generar la copia de sí misma.

Vamos a instanciar nuestra clase, con propiedades específicas, y después haremos una copia:

Ya tenemos nuestro objeto clonado, por lo que a partir del nuevo objeto clonado ya podemos realizar cambios:

Sencillo, ¿no?…

— Ejemplo completo

:: Ejemplo 2

Ahora… qué pasaría si tenemos propiedades que no se crean a partir del constructor (como en el ejemplo anterior), tomemos alguno de nuestros ejemplos ya hechos:

A la clase Car agregamos el método encargado de clonarse a sí mismo:

Hagamos la instancia de objeto y el clonado :

— Ejemplo completo

Podemos observar en la salida de la descripción que no ha clonado las propiedades model, seats, slidingSunroof; para lograr esto tenemos que hacer unas pequeñas modificaciones a nuestro método clone.

Importante: Esta solo es una forma (de tantas) para clonar todo el objeto, puede que no sea la ideal o la más correcta…

Lo primero es agregar un nuevo método dentro de la clase Car, que va a ser la encargada de recibir una propiedad y un valor:

Ahora hacemos unos cambios en nuestro método clone:

Ahora, en nuestro método clone, vamos a obtener todas las propiedades que tiene la clase instancia, recorrerlas y asignarlas al objeto clonado:

Si ejecutamos de nuevo nuestro ejemplo, ahora sí debemos el clonado del objeto completo.

— Ejemplo completo

Importante: En este ejemplo no se tiene contemplado propiedades tipo objeto, por lo que se corre el peligro de que ambos objetos apunten a una misma referencia (ver Shallow copy and Deep copy[ref]).

Se recomienda usar cuando…

  • Se pueden generar grupos de propiedades.
  • Cuando queremos clonar objetos sin acoplar a sus clases.

La ventaja de usarlo…

  • Nos permite utilizar objetos preconstruidos.
  • Hay un aumento de rendimiento y mantenimiento.
  • Se pueden producir objetos complejos a nuestras necesidades.
  • Se obtiene una alternativa a la herencia.

La desventaja de usarlo…

  • Al clonar objetos complejos corremos el riesgo de tener referencias circulares.
  • Elementos de los objetos apuntando a una misma referencia.

El patrón Prototype puede ser útil cuando necesita guardar copias de clases en el historial (parecido al state).

iv. Creational — Object — Singleton

El patrón de Singleton sirve cuando necesitamos una sola instancia de clase; su uso más común es cuando estamos compartiendo archivos o acceso a una base de datos, o cuando queremos controlar ciertas características a nivel global dentro de nuestra app.

:: Ejemplo

Imagina que hemos creado la instancia de un objeto (conexión a una base de datos), después a lo largo del código decidimos crear una nueva conexión, en lugar de recibir una instancia nueva, esta nos va a devolver la que ya se creó…

Veamos un ejemplo sencillo:

Cuando hacemos la instancia de objeto por primera vez:

Si queremos crear una nueva instancia de la clase que ya ha sido instanciada:

Podemos observar que no afecta la propiedad al momento de crear una nueva instancia.

— Ejemplo completo

Bonus: Veamos un ejemplo con una expresión de función, pero autoejecutable como IIFE[ref].

Se recomienda usar cuando…

  • En nuestra aplicación necesitamos tener una sola instancia de algún objeto sin importar en qué parte de la aplicación se encuentre.
  • Cuando queremos tener un control de variables globales

La ventaja de usarlo…

  • Nos aseguramos que una clase específica va a tener una sola instancia
  • La instancia del objeto sólo ocurre la primera vez

La desventaja de usarlo…

  • No cumple con el principio SOLID (principio de responsabilidad única)
  • Que una mala práctica puede borrar la instancia que tenemos.
  • Las pruebas unitarias pueden generar problemas.

Los patrones de Abstracts Factory, Constructores y Prototype se pueden implementar como Singletons.

--

--

No responses yet