¿Cómo implementar el patrón AbstractFactory en JavaScript?
El patrón AbstractFactory es una robusta herramienta para la creación de familias de objetos relacionados. En este artículo, presentamos la implementación práctica de este patrón orientado a objetos en JavaScript, destacando su poder para disminuir el acoplamiento entre el código cliente y las instancias de los productos.
¿Cómo definir las clases base de los productos?
Para arrancar con el AbstractFactory, es fundamental definir las clases o interfaces de los productos base. En este caso, definimos dos productos: Mastodon y Rhino. Cada producto implementa un método useGPS, lo que garantiza que las implementaciones futuras deban realizar este método.
classMastodonCard{useGPS(){thrownewError("method not implemented");}}classRhinoCard{useGPS(){thrownewError("method not implemented");}}
¿Cómo creamos las clases concretas de los productos?
El siguiente paso es establecer las clases concretas de los productos, que heredan de las clases base. Aquí creamos dos familias de vehículos: Sedan y Hatchback. Cada familia tiene su correspondiente implementación para Mastodon y Rhino.
classMastodonSedanCardextendsMastodonCard{useGPS(){console.log("Sedan Mastodon GPS");}}classRhinoSedanCardextendsRhinoCard{useGPS(){console.log("Sedan Rhino GPS");}}// Implementaciones similares para Hatchback
¿Qué es el abstract factory?
Posteriormente, se define el abstract factory, que establece un contrato para la creación de nuestras instancias de producto.
classAbstractFactory{createMastodon(){thrownewError("method not implemented");}createRhino(){thrownewError("method not implemented");}}
¿Cómo implementar las fábricas concretas?
La clave del patrón está en las fábricas concretas que heredan del abstract factory. Definen las específicas versiones de los productos dentro de una familia.
classSedanCarFactoryextendsAbstractFactory{createMastodon(){returnnewMastodonSedanCard();}createRhino(){returnnewRhinoSedanCard();}}// Implementación similar para Hatchback
¿Cómo utilizar el patrón abstract factory?
Al emplear el patrón AbstractFactory, se inyecta la fábrica deseada, permitiendo crear objetos sin conocer su precisa clase. En el siguiente ejemplo utilizamos la fábrica Sedan y la Hatchback, y llamamos al método useGPS() de modo genérico.
¿Qué podemos concluir sobre la implementación en JavaScript?
Esta implementación del patrón AbstractFactory demuestra cómo se pueden crear distintos tipos de objetos relacionados sin comprometer el código cliente con clases específicas. Este patrón facilita que el diseño del software sea flexible, manejable y extensible.
Comparación con TypeScript y otras consideraciones
En proyectos complejos, utilizar TypeScript puede ofrecer ventajas adicionales, como la verificación temprana de tipos que ayudan a detectar errores. Continuaremos explorando las particularidades y beneficios de implementar este patrón en TypeScript en futuras entregas.
Esta metodología no sólo ilustra un claro entendimiento de los patrones de diseño, sino que también se erige como una herramienta esencial en el arsenal de cualquier desarrollador. Te animo a que implementes este patrón en tus propios proyectos y descubras por ti mismo sus beneficios. ¡Continúa aprendiendo y mejorando tus habilidades!
No logró pensar en donde podría aplicar yo este patrón, en algunos empleos lo piden, pero como se vio en el curso anterior quizá es una exageración el pedirlo.
Pero con este patrón si entendí algo, los desarrolladores de zomboid son expertos en POO, porque puedo ver como este patrón ayudo a plantear ciertas mecánicas de ese videojuego, por ejemplo el "nuevo" sistema de autos en zomboid está claramente basado en este tipo de factories y zomboid es un juego mucho más complejos a nivel de mecánicas que la mayoría de juegos del mismo tipo.
Wow estoy fascinado con al relación que acabas de encontrar entre la teoria y las aplicaciones en la industria.
Ciertamente hay patrones que no son muy usados, y a veces utilizarlos podria ser forzado (me ha pasado).
AF surge como una solucion oportuna cuando requires estandarizar comportamiento a traves de multiples versiones de un objeto. Los ejemplos que siempre se presentan en este patron estan alrededor del mundo de los videojuegos, le diste justo al blanco.
Ahora que lo comentas me resulta más claro, las fábricas de unidades de los RTS con claramente este patrón, no importa que tipo de unidad sea, se puede abstraer a una unidad y realizas líneas de producción que especialice a la unidad, simplemente se agregan requerimientos dependiendo de la especialización.
WOW!!!!, este era de los temas que más me daban miedo, pero bien explicados resultan fascinantes, solo queda desarrollar la experiencia y para poder aplicarlos cuando se necesitan.
En lo personal este patrón no me encanta. Factory ya usaba una capa de abstracción y este agrega otra capa (lo cuál lo hace complejo).
Además de eso, para agregar un producto nuevo (un coche en este caso), se necesitan agregar muchas cosas:
La creación del coche que siga el contrato de BaseCar (por cierto, no se agrego un BaseCar en el ejemplo de esta clase)
La creación de cada uno de los modelos de nuevo coche (Sedan y Hatchback en este caso)
Agregar un nuevo método en AbstractFactory para el nuevo producto
Agregar polimorfismo a cada subfactory del nuevo producto (SedanModelFactory & HatchbackModelFactory)
.
Son muchas cosas para agregar un nuevo producto. Aunque la buena noticia es que se siguen cumpliendo los principios de Responsabilidad Unica y de Open-Close
Para mí no es que me agrade o me desagrade. Solo veo que puede ser una buena solución (aunque sí, quizás tediosa) para un problema complejo de crear y operar elementos y subelementos, como un elemento A1, A2, A3, B1, B2...
Y lograrlo con una buena mantenibilidad y extensibilidad, si así lo requiere el proyecto, claro.
Muy interesante este patrón de diseño, creería que es un patrón que podemos utilizar cuándo tenemos lógicas de negocio complejas.
Dejo mi codigo, a modo de experimento tengo algunas cosas distintas:
classNivusBase{model(){}}classTcrossBase{model(){throwError("This method is not implemented");}}classNivusSedanextendsNivusBase{model(){return"Nivus Senda 2023";}}classNivusHatbackextendsNivusBase{model(){return"Nivus Hatback 2023";}}classTcrossSedanextendsTcrossBase{model(){return"Tcross Sedan 2023";}}classTcrossHatbackextendsTcrossBase{model(){return"Tcross Hatback 2023";}}classCarAbstractFactory{createNivus(){throwError("This method is not implemented");}createTcross(){throwError("This method is not implemented");}}classSedanCarFactoryextendsCarAbstractFactory{createNivus(){returnnewNivusSedan();}createTcross(){returnnewTcrossSedan();}}classHatbackCarFactoryextendsCarAbstractFactory{createNivus(){returnnewNivusHatback();}createTcross(){returnnewTcrossHatback();}}functionappAbstractFactory(factory){returnnewfactory()}carSedanFactory =appAbstractFactory(SedanCarFactory);carHatbackFactory =appAbstractFactory(HatbackCarFactory);console.log(carSedanFactory.createNivus().model());console.log(carSedanFactory.createTcross().model());console.log(carHatbackFactory.createNivus().model());console.log(carHatbackFactory.createTcross().model());
En el paso 4 noté que faltó heredar de la clase CarAbstractFactory para cumplir con lo que nos dice este paso: Create concrete factories that implements/inherits from the abstract factory behaviour and implements all the products creation methods.
Cada vez más preparados para ir a aplicar en Tesla 😎
Grandes aspiraciones! Go for it Diego!
hola tengo una pregunta,¿ las constantes se crean en un entonrno local, entonces como la appCarFactory se llama 2 veces, tendriamos 2 contantes llamadas rino y 2 llamadas mastodont, cada par rinho mastodont en un contexto local? como podria acceder a cada producto particular creado en cada llamada del appCarFactory?
como sacas los multiples cursores para editar varias lineas de codigo al tiempo ? s no tengo mouse :V
pulsas ALT mientras haces click en las lineas q vas a editar.
hola nestor, gracias por tomarte el tiempo pero, no puedo usar el mouse, se que con crtl D se repiten los cursores en las ocurrencias pero queria saber si hay otra forma que no sea solo para ocurrencias
Un poco díficil de entender en la última parte, en particular en el siguiente fragmento de código:
functioncreateFactory(type){// ....constFactory= factories[type];returnnewFactory();}```Creo que se debe especificar que la constante Factory, ahora contiene una referencia a la clase o función constructora que fue seleccionada en el paso anterior. Luego se crea una nueva instancia de esa clase o función constructora usando `newFactory()`.
El que no haya extendido las clases HatchbackCarFactory y SedanCarFactory a CarAbstractFactory es un error? Si no fuese un error, no coincidiria con el diagrama de la clase anterior.
Toda la razon Abelardo, se me fue, gracias por notarlo!