Entendiendo el patrón Abstract Factory
Mejora la arquitectura de tus proyectos PHP: domina el patrón Abstract Factory para un código más eficiente y organizado.
El patrón Abstract Factory provee una interfaz para crear familias de objetos relacionados o dependientes sin especificar sus clases concretas, y es un patrón de diseño creacional.
Imagina que es una fábrica de fábricas; donde cada "fábrica" puede crear diferentes tipos de objetos que están interconectados.
Ejemplo Práctico
¿Sería posible que tener tanto una "Fábrica de Computadoras para Gamers" como una "Fábrica de Computadoras para Edición de Video"?
Primero, definimos interfaces para nuestros productos, es decir, los componentes de las computadoras:
1interface Procesador {2 public function getVelocidad();3}4 5interface GPU {6 public function getMemoria();7}
Luego, implementamos estas interfaces para crear productos concretos:
1class ProcesadorGamer implements Procesador { 2 public function getVelocidad() { 3 return "4.5 GHz"; 4 } 5} 6 7class ProcesadorEdicion implements Procesador { 8 public function getVelocidad() { 9 return "3.8 GHz optimizado para multitarea";10 }11}12 13class GPUGamer implements GPU {14 public function getMemoria() {15 return "12 GB";16 }17}18 19class GPUEdicion implements GPU {20 public function getMemoria() {21 return "8 GB, optimizado para renderizado";22 }23}
Ahora, pasamos a crear la Abstract Factory que define los métodos para crear estos productos:
1interface ComputadoraFactory {2 public function crearProcesador(): Procesador;3 public function crearGPU(): GPU;4}
A partir de este punto pasamos a implementar nuestra interfaz de la siguiente forma:
1class ComputadoraGamerFactory implements ComputadoraFactory { 2 public function crearProcesador(): Procesador { 3 return new ProcesadorGamer(); 4 } 5 6 public function crearGPU(): GPU { 7 return new GPUGamer(); 8 } 9}10 11class ComputadoraEdicionFactory implements ComputadoraFactory {12 public function crearProcesador(): Procesador {13 return new ProcesadorEdicion();14 }15 16 public function crearGPU(): GPU {17 return new GPUEdicion();18 }19}
Usando el factory
Para usar nuestro factory solo debemos hacerlo de forma parecida a esta:
1function fabricarComputadora(ComputadoraFactory $factory) { 2 $procesador = $factory->crearProcesador(); 3 $gpu = $factory->crearGPU(); 4 5 echo "Procesador: " . $procesador->getVelocidad() . "\n"; 6 echo "GPU: " . $gpu->getMemoria() . "\n"; 7} 8 9// gamer pc10fabricarComputadora(new ComputadoraGamerFactory());11 12// editar video13fabricarComputadora(new ComputadoraEdicionFactory());
Y nos va retornar lo siguiente:
1Procesador: 4.5 GHz2GPU: 12 GB3Procesador: 3.8 GHz optimizado para multitarea4GPU: 8 GB, optimizado para renderizado
¿Por qué es útil?
- Al agrupar la creación de objetos naturalmente relacionados, se promueve la cohesión.
- Utiliza interfaces en lugar de clases específicas para reducir el acoplamiento entre tu código y las clases concretas.
- Permite añadir nuevas variantes de productos sin afectar el código cliente existente, siguiendo el principio de abierto/cerrado.
- Por lo tanto, siempre que necesites crear familias de productos o conceptos relacionados, deberías considerar el uso del patrón Abstract Factory. Funciona como una forma elegante de mantener tu código organizado, flexible y escalable.
¿Cuándo aplicar el patrón Abstract Factory?
Puede no ser inmediatamente evidente cuándo aplicar el patrón Abstract Factory, pero hay varias pistas y situaciones que pueden indicarte que es una buena opción considerarlo.
Categorías de productos similares
Si deseas que las "familias" de productos relacionados o dependientes entre sí sean coherentes, el patrón Abstract Factory es ideal para tu aplicación. Esto es especialmente verdadero cuando estas familias de productos están destinadas a ser utilizadas en conjunto.
Pista: Posees varios objetos o productos que se utilizan en conjunto y tienen variaciones dependiendo del contexto (por ejemplo, componentes de interfaz de usuario para distintos sistemas operativos, diversos tipos de objetos para distintas configuraciones de juego, etc.).
Necesidad de abstracción
Si tu proyecto requiere trabajar con múltiples variantes de productos, pero no debe depender directamente de las clases específicas para crear esos productos. El uso de patrones te permite trabajar a un nivel de abstracción más alto, empleando interfaces para definir las acciones que puedes realizar con los productos sin detallar su implementación.
Pista: Estás escribiendo código que debería poder adaptarse fácilmente a nuevas variantes de productos sin necesidad de realizar muchos cambios.
Separación de la lógica de creación
Si necesitas separar la lógica de creación de tus productos del código que los utiliza, el Abstract Factory puede ser útil. La creación de objetos se encapsula en fábricas que son implementaciones de una interfaz común, gracias a este patrón.
Pista: ¿Te gustaría separar la construcción de objetos de su uso, para hacer tu código más modular y fácil de mantener?
Inversión de Dependencia es un principio fundamental.
Este principio establece que la dependencia de tu código debe ser en abstracciones, no en clases concretas. Si tu código comienza a depender en exceso de los detalles específicos de la creación de objetos, puede ser el momento adecuado para pensar en Abstract Factory.
Pista: ¿Estás en la búsqueda de formas para hacer tu código más flexible y menos acoplado, al mismo tiempo que respetas los principios SOLID?
Frecuentes cambios en familias de productos.
Si prevés que las familias de productos utilizadas por tu aplicación podrían cambiar con frecuencia o que puedas necesitar agregar nuevas familias en el futuro, el patrón Abstract Factory puede ayudarte a manejar esos cambios de manera más fluida.
Pista: Es necesario que tu aplicación sea escalable y pueda adaptarse a la integración de nuevas líneas de productos sin tener que realizar grandes modificaciones en el código ya existente.
Cómo aplicarlo efectivamente
Si encuentras una o varias de estas señales en tu proyecto, deberías considerar utilizar el patrón Abstract Factory. Comienza definiendo interfaces comunes para tus familias de productos y luego implementa estas interfaces en clases concretas que representen variantes específicas de los productos. Por fin, establece fábricas concretas que engloben la fabricación de estas variaciones de productos.
Ten en cuenta que el propósito principal de este patrón es aumentar la modularidad y flexibilidad de tu código, haciendo que sea más sencillo extenderlo y mantenerlo a lo largo del tiempo.