¿Qué es el patrón de diseño Factory? 🦺
¿Qué es el patrón de diseño factory?
El patrón de diseño Factory (Factory Method) es un patrón de diseño creacional que se utiliza en desarrollo de software para crear objetos de manera más flexible y modular.
El objetivo principal de este patrón es abstraer la lógica de creación de objetos de la aplicación y colocarla en una clase separada (la "fábrica") para que sea más fácil agregar nuevas clases y cambiar la forma en que se crean los objetos sin afectar el resto del código.
En términos generales, el patrón Factory funciona de la siguiente manera:
- Se crea una interfaz común para representar los objetos que se van a crear.
- Se crea una clase "fábrica" que define un método de fábrica abstracto para crear objetos de la interfaz común.
- Se crean varias clases que implementen la interfaz común,por lo que proporciona su propia implementación de los métodos definidos en la interfaz.
- La clase "fábrica" también tiene varias subclases, cada una de las cuales implementa el método de fábrica para crear un objeto de una de las subclases de la interfaz común.
- En el código de la aplicación, se utiliza la clase "fábrica" para crear objetos de la interfaz común sin necesidad de conocer la clase concreta que se está creando.
Este patrón de diseño se utiliza comúnmente en situaciones donde se necesita crear varios tipos de objetos que comparten una interfaz común, como por ejemplo, en la creación de objetos de una clase padre que tiene varias subclases con diferentes funcionalidades.
El patrón Factory permite que el código sea más flexible y fácil de mantener al permitir la creación de nuevos objetos sin tener que modificar el código existente.
Te voy a explicar con un ejemplo, siempre queda más claro.
Patrón de diseño factory en el juego dino de Google Chrome
Imagina que queremos programar el juego del dinosaurio que Google Chrome te ofrece cuando no tienes conexión a Internet.
El objetivo del juego es ir superando obstáculos con dino.
Para jugar al juego, escribe chrome://dino/
en la barra de navegación de tu Google Chrome.
Para simplificar, vamos a suponer que existen 2 tipos de obstáculos:
- 🌵 Cactus
- 🐦 Pájaro
Vamos a crear una clase por cada obstáculo, que como son obstáculos, implementarán la interface Obstaculo
interface Obstaculo { actualizaPosicion():void; } class Cactu implements Obstaculo { public actualizaPosicion():void { Console.log("Soy un cactu, me desplazo junto al paisaje!"); } } class Pajaro implements Obstaculo { public actualizaPosicion():number { Console.log("Soy un pájaro y me desplazo al doble que el escenario"); } }
Esto nos permitirá aplicar el polimorfismo, tratando a todos los obstáculos por igual 👇
void logicaJuego(obstaculos:Obstaculo[]){ obstaculos.foreach(obstaculo=>{ obstaculo.actualizaPosicion() }) }
Para generar de forma aleatoria los obstáculos y que no aparezcan siempre en el mismo orden los generaremos con una función random. Con ello conseguiremos sorprender al jugador.
const numero_aleatorio=Math.random(); const obstaculo:Obstaculo; if(numero_aleatorio>0.5){ obstaculo=new Cactu(); }else{ obstaculo=new Pajaro(); }
Ahora imagina que quieres aumentar la dificultad, suponiendo que mientras más pájaros salgan, más difícil será.
Tienes la opción de modificar tu código actual perjudicando a los jugadores que no quieran más dificultad.
Lo que nos permite el patrón factory, es este caso, es encapsular la estrategía de fabricación de obstáculos.
La clase Factory sería la responsable de mantener la lógica de la creación de enemigos.
En este ejemplo, podríamos tener dos clases factory, una que será la que genere obstáculos con la misma probabilidad y otra clase que generara más pájaros para aumentar la dificultad del juego.
interface ObstaculoFactory(){ creaObstaculo():Obstaculo } class obstaculosAleatorios implements ObstaculoFactory{ creaObstaculo():Obstaculo{ const numero_aleatorio=Math.random(); const obstaculo:Obstaculo; if(numero_aleatorio>0.5){ obstaculo=new Cactu(); }else{ obstaculo=new Pajaro(); } return obstaculo; } } class obstaculosDificiles implements ObstaculoFactory{ creaObstaculo():Obstaculo{ const numero_aleatorio=Math.random(); const obstaculo:Obstaculo; if(numero_aleatorio>0.2){ obstaculo=new Cactu(); }else{ obstaculo=new Pajaro(); } return obstaculo; } }
Con esto, hemos generalizado el mecanismo de crear obstáculos a una interfaz general.
Ejemplo con vehículos
Supongamos que queremos crear diferentes tipos de vehículos, como "Coche", "Avión" y "Barco".
Podemos usar el patrón Factory para crear objetos de estos tipos sin exponer la lógica de creación de objetos en el código que utiliza estos objetos. Primero, definimos una interfaz "Vehículo" que define las propiedades y métodos que todos los vehículos deben tener:
interface Vehiculo { ruedas:number; puedeVolar(): void; }
A continuación, creamos las clases "Coche", "Avión" y "Barco" que implementan la interfaz "Vehiculo":
class Coche implements Vehiculo { ruedas: number; constructor() { this.ruedas = 4; } puedeVolar() { console.log(Soy un coche y no puedo volar); } } class Avion implements Vehiculo { ruedas: number; constructor() { this.ruedas = 8; } puedeVolar() { console.log(Soy un avión y sí puedo volar); } } class Barco implements Vehiculo { ruedas: number; constructor() { this.ruedas = 0; } puedeVolar() { console.log(Soy un barco y no puedo volar); } }
Por último, creamos una fábrica "VehiculoFactory" que crea objetos "Coche", "Barco" y "Avión" según el tipo que se le pase como argumento:
class VehiculoFactory { creaVehiculo(type: string, ruedas: number): Vehiculo { switch (type) { case "Coche": return new Coche(); case "Barco": return new Barco(); case "Avión": return new Avion(); default: throw new Error("Tipo de vehículo no válido."); } } }
Ahora podemos utilizar la fábrica "VehiculoFactory" para crear diferentes tipos de vehículos de manera sencilla:
Hasta luego 🖖