Implementar Factory en PHP El patrón de diseño Factory te permite centralizar la creación de objetos en común en tu aplicación para desacoplar la relación entre clases...

En palabras mortales, quiere decir que si tenemos una clase como:

Podemos ver la clase Carro y Motor están acopladas, es decir que la clase “Motor” es invisible al usuario, y en dado caso que queramos cambiar el “motor” del “carro” tendríamos que modificar el código fuente de la clase “Carro”, lo cual viola uno de los principios de la programación orientada a objetos que es la re-usabilidad.

Para desacoplar las clases podríamos, por ejemplo, pasar la clase Motor como parámetro en el constructor de la clase Carro (aka: Inyeccion de Dependencias):

De esta forma, si queremos cambiar el “motor” del “carro” solo tenemos que extender la clase “Motor” con el nuevo tipo de motor, e inyectarlo en el constructor de la clase carro:

Y listo! delegamos la creación de objetos al programador e implementamos Inyección de dependencias satisfactoriamente!

pues no es cierto adal ramones

El problema es que si creamos un “Carro” nuevo e inyectamos el objeto “Motor” al carro nuevo, en realidad le estamos poniendo un “Motor” usado. En otras palabras todos las instancias de “Carro” van a compartir la misma instancia de “Motor”.

$ php index.php
run run run run...

PHP Fatal error:  Uncaught exception 'Exception' with message 'Ehh!! Ya estoy prendido me vas a descomponer!!' in /Users/achavez/Programs/tests/phps/Factory/index.php:10

Stack trace:
#0 /Users/achavez/Programs/tests/phps/Factory/index.php(23): Motor->arranca()
#1 /Users/achavez/Programs/tests/phps/Factory/index.php(30): Carro->__construct(Object(Motor))
#2 {main}
thrown in /Users/achavez/Programs/tests/phps/Factory/index.php on line 10

Fatal error: Uncaught exception 'Exception' with message 'Ehh!! Ya estoy prendido me vas a descomponer!!' in /Users/achavez/Programs/tests/phps/Factory/index.php:10

Stack trace:
#0 /Users/achavez/Programs/tests/phps/Factory/index.php(23): Motor->arranca()
#1 /Users/achavez/Programs/tests/phps/Factory/index.php(30): Carro->__construct(Object(Motor))
#2 {main}
thrown in /Users/achavez/Programs/tests/phps/Factory/index.php on line 10

Oops! Hay un error porque los dos carros están compartiendo el mismo “motor” (en PHP los objetos se pasan por referencia automáticamente) por lo que al momento de querer prender el motor dos veces, el programa nos envía una excepción!

Es en situaciones como la anterior cuando podemos utilizar una Factory para desacoplar dos clases y que cada instancia de la clase “principal” tenga su dependencia exclusiva. En otras palabras, cada Carro tiene que tener su propio Motor.

Para ilustrar el verdadero “poder” del patron de diseño Factory vamos a crear múltiples “Carros” con diferentes tipos de Motores (Gasolina, Diesel y Biodiesel) con una cantidad variable de cilindros.

El patrón Factory se identifica porque tienen dos partes. Por un lado tenemos las clases “producto” y por el otro lado tenemos las clases “creadoras”.

El Producto

Así como en las fábricas de verdad (en las que trabajan personas y manufacturan productos), el patrón Factory tiene un prototipo de lo que se va a construir. Dicho prototipo usualmente se define como una clase abstracta, y todos los objetos que el patrón Factory crea extienden dicha clase abstracta. La clase abstracta y las clases que extienden la clase abstracta son la parte “producto” del patrón Factory:

En la literatura, todas las clases que extienden a los "prototipos", se les conoce como clases concretas:

La clase "creadora" o Factory

Una vez que definimos el/los productos y el/los productos concretos que las Factory van a generar, pasamos al lado “Creador” de éste patrón de diseño, que es la Factory en si:

NOTA: En éste ejemplo estoy declarando la Factory como una clase convencional que requiere instanciarse. Sin embargo, el método crearCarro está declarado como estático, por lo que podemos crear instancias de Carros, sin tener que instanciar la clase CarrosFactory. Finalmente, implementamos el “cliente” quién es el que recibe los “productos” que las “factory” producen:

Si lo ejecutamos, este es el resultado:

$ php CarrosFactory.php
Encendiendo motor ... run run run
Encendiendo motor ... run run run
Encendiendo motor ... run run run
Encendiendo motor ... run run run

Ventajas

En el ejemplo anterior implementar una factory fue un “overkill”. Sin embargo, se puede ilustrar el beneficio de éste patrón de diseño al introducir una clara una separación entre los diferentes tipos de clases, y al mismo tiempo proveer una manera simple de extender la familia de “productos” sin cambios mayores al código original.

Por ejemplo: si quisiéramos crear un nuevo tipo de Motor, solamente tenemos que crear las clases CarroElectrico y MotorElectrico, y agregar un nuevo caso al switch de CarrosFactory y listo. De no haber utilizado el patrón factory, hubiéramos tenido que modificar la clase “Carro” y posiblemente hacer muchas modificaciones al código para que funcionara con un nuevo tipo de “motor”.

Por otra parte, si la creación de un solo objeto requiere multiples lineas y ése objeto se tiene que crear muchas veces en el código de tu aplicación, quizá es conveniente implementar una Factory y de paso mejoras la legibilidad de tu programa y disminuyes el costo de mantenimiento del mismo.

Desventajas

La desventaja del patrón Factory, es que aún existe un acoplamiento entre los productos concretos, sin embargo ya no es un acoplamiento tan estricto como el que se tenía antes de haber implementado la Factory.

Otra "deventaja" de éste patrón es que introduce mucha complejidad al sistema para una tarea relativamente simple. Es importante tener en mente que al implementar una Factory estamos centralizando la creación de objetos comunes a un solo lugar, pero ten en cuenta que al realizar este proceso de centralización vas a incrementar complejidad e incurrir en overhead.

Conclusion

No utilices una Factory para crear objetos que solamente vas a utilizar una o dos veces en tu código. Solo introduces complejidad, redundancia y overhead innecesario.

Como siempre, espero que éste artículo te haya aclarado tus dudas y sirva como referencia en el futuro. Si encuentras un error en el código, conoces una manera más eficiente de implementar Factories en PHP o simplemente quieres decir "hola" déjame un comentario!