Patrones de diseño creacionales

Share on facebook
Share on twitter
Share on linkedin

En el desarrollo de Software se usan patrones de diseño para resolver problemas recurrentes utilizando soluciones estándar, el universo de los patrones es infinito, cada día salen nuevos; no se trata de conocer y dominar todos, el punto es saber cuándo aplicar alguno, tener presente que el patrón se debe adaptar al proyecto, y no al revés.

El conocerlos no te hace mejor o peor en desarrollo, existen desarrolladores que trabajan años en empresas y elaboran productos de calidad sin aplicar o conocer los patrones de diseño. Pero como desarrollador, siempre es bueno saber que existen patrones, saber que cuando se te presente un problema, tienes la opción de buscar si un patrón lo resuelve.

¿Qué es un patrón de diseño?

A principios de los años noventa por The Gang of For (GoF): Erich Gamma, Richard Helm, Ralph Johnson Y John Vlissides en su libro Design patterns: Elements of reusable object-oriented software, definen un patrón como:

Una serie de eventos que se van haciendo de manera recurrente, es decir, una sucesión de eventos predecibles.

De igual manera, mencionan que un patrón de diseño se compone de un nombre (que debe ser muy claro, usualmente dice lo que hace), describe de manera general el problema a trabajar y la solución debe estar definida en función del problema. Por último, señalan tres categorías para agrupar los patrones de diseño: creacionales, estructurales y de comportamiento.

Antipatrón

A veces en nuestro intento de crear cosas de calidad, llenos de teoría de buenas prácticas podemos caer en un antipatrón, es decir, implementar un patrón de diseño que nos guie a una mala solución… ¿Cómo evitarlos? La respuesta siempre será la experiencia, sin embargo, hay un par de puntos que debes considerar antes de aplicar algún patrón de diseño a tus proyectos:

  • Si no ves dónde aplicarlo, entonces no lo necesitas.
  • Si no conoces las ventajas y desventajas, mejor no lo implementes.
  • Si al aplicarlo te genera más puntos negativos que positivos (o incluso iguales) es mejor no implementarlo.

En este artículo abarcaremos los patrones de diseño creacionales, que proveen técnicas para crear objetos. Antes de continuar, es importante destacar que los patrones de diseño son agnósticos al lenguaje de programación, lo importante es entender el concepto del patrón, por lo que los ejemplos mostrados rondarán entre Javascript Y Typescript según sea más conveniente.

Patrón de diseño: Singleton

Objetivo: Crea un único elemento y mantenerlo durante el tiempo deseado en la ejecución del programa.

Principalmente, el patrón singleton te permite crear una instancia de un objeto una vez, y luego usarlo cada vez que lo necesite, en lugar de crear uno nuevo sin tener que hacer un seguimiento de una referencia a él, ya sea globalmente o simplemente pasándolo como una dependencia en todas partes.

En Javascript, se puede implementar de dos maneras, mediante el uso de IIFE o mediante el uso de clases de ES6 (forma que personalmente prefiero). La implementación quedaría de la siguiente manera:

Y puede ser usado de la siguiente manera:

Casos de uso

Cuando se está por decidir si es buena idea implementar este patrón, pregúntese: ¿Cuántas instancias de la clase voy a necesitar realmente? Si la respuesta es 2 o más, entonces este no es su patrón. El caso de uso más común es la conexión a base de datos, una vez que se haya conectado, es una muy buena idea mantener esa conexión activa y accesible, eso sí, este problema se puede resolver de muchas otras maneras y este patrón es de hecho, una de ellas.

Patrón de diseño: Factory

Objetivo: Crear instancias de objetos de otras clases mediante la encapsulación de la creación de los mismos.

Este patrón suele ser confuso, puesto que tiene dos vertientes principales: el factory method y el abstract factory. Pero ambas vertientes se componen de las mismas partes:

  • Producto: Define la interfaz de los objetos que crea el método de fabricación.
  • Producto concreto: Implementa la interfaz del producto.
  • Creador: Declara los métodos para fabricar y devuelve un objeto solicitado.
  • Creador concreto: Redefine el método de fabricación para devolver una instancia del producto concreto.

Factory method

En esencia, el factory method te permite centralizar la lógica de crear objetos (es decir, qué objeto crear y por qué) en un solo lugar, y con ello, que te olvides de esa parte, para centrarte en simplemente solicitar el objeto que se necesita y luego usarlo.

Ahora la implementación real, es bastante sencilla:

Casos de uso

Un caso de uso particular para el que me gusta usar este patrón, es el manejo de la creación de objetos de error. En mis proyectos suelo manejar objetos de error que tienen como atributos un mensaje, código http y un código de error específico de aplicación en función de las entradas recibidas, estos atributos varían, por lo que al tener centralizada la lógica de creación de estos objetos, facilita la extensión de atributos de los mismos.

Abstract factory

Mejor conocido por ser una fábrica de fábricas.

Objetivo: Encapsular un grupo de fábricas individuales con un objetivo común. Separa los detalles de la implementación de un conjunto de objetos de su uso general.

Casos de uso

Se debe usar donde un sistema tiene que ser independiente de la forma en que se generan los objetos que crea, o si necesita trabajar con múltiples tipos de objetos.

Patrón de diseño: Builder

Objetivo: Permite construir un objeto indicando, lo que debe hacer para realizar su trabajo.

El patrón Builder permite a un cliente construir un objeto complejo especificando sólo el tipo y el contenido. Los detalles de construcción están ocultos del cliente por completo.

La motivación más común para usar Builder, es simplificar el código del cliente que crea objetos complejos. El cliente aún puede dirigir los pasos tomados por el constructor sin saber cómo se realiza el trabajo real. Los constructores con frecuencia encapsulan la construcción de objetos compuestos (otro patrón de diseño de GoF), porque los procedimientos involucrados son a menudo repetitivos y complejos.

Por lo general, es el último paso el que devuelve el objeto recién creado, lo que facilita que un constructor participe en interfaces fluidas en las que múltiples llamadas de métodos, separadas por operadores de punto, se encadenan juntas.

Veamos un ejemplo:

Y se usaría de la siguiente manera:

Prestemos atención a esta línea:

const course_1 = new Course('Design Patterns 1', 0, true, 149 , true);

¿Podrías decir lo que significa 0, true, 149, true? Seguramente no. Debes ir al constructor de la clase Course cada vez, para ver a qué valor está vinculado cada parámetro. Este problema puede ser resuelto fácilmente por el patrón Builder. Si la línea anterior estuviera representada en un patrón Builder, entonces se vería así:

const course_1 = new CourseBuilder('Design Patterns 1').makePaid(100).makeCampain().build();

Refactorizando nuestra clase implementando el patrón Builder, ahora luciría así:

Y se usaría de la siguiente manera:

Casos de uso

Como puedes notar un Builder es útil cuando necesita hacer muchas cosas para construir un objeto. Claro, el número de líneas de código aumenta al menos al doble en el patrón Builder, pero el esfuerzo vale la pena en términos de flexibilidad de diseño y código mucho más legible.

Patrón de diseño: Prototype

Objetivo: Especifica los tipos de objetos para crear utilizando una instancia prototípica y crea nuevos objetos copiando este prototipo.

El patrón Prototype crea nuevos objetos, pero en lugar de crear objetos no inicializados, devuelve objetos que se inicializan con valores que copió de un prototipo u objeto de muestra. El patrón de Prototype también se conoce como el patrón de propiedades.

Los lenguajes clásicos rara vez usan el patrón Prototype, pero JavaScript, como lenguaje prototípico, usa este patrón en la construcción de nuevos objetos y sus prototipos.

Una implementación quedaría de la siguiente manera:

Y se usaría de la siguiente manera:

Casos de uso

Como puedes notar, este patrón es útil para la creación de objetos repetitivos, evitar subclases y evitar la creación de las mismas.

Conclusiones

No se necesita mucha experiencia en el campo laboral para darse cuenta de que todos codifican de manera diferente y conocer estas soluciones, evitará que debas reinventar la rueda en alguno de tus proyectos. Pero antes de implementar alguno de estos patrones, debes conocer muy bien las mejoras que traería el patrón a tu proyecto, si no lo haces, es mejor no aplicarlo. Es mejor saber cuándo no implementar un patrón.

Share on facebook
Share on twitter
Share on linkedin

No hay comentarios

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

¡Conozcámonos mejor!

Te haremos llegar las novedades de SoldAI, ofertas exclusivas, notificaciones, y mucho más.

¡Deja tu correo, tenemos mucho que contarte!