domingo, 13 de noviembre de 2016

Factory Method. Parte III - Variante en PHP (u otros lenguajes)

Después de ver el caso más tradicional del patrón factory method, donde cada producto concreto tenía que tener una subclase del creador asociada, vamos a ver una variante para aplicar con lenguajes como PHP.

El principal problema del factory method reside en que, si tenemos muchos subproductos, vamos a tener una gran cantidad de subclases constructoras de esos productos, además de que la clase cliente debe heredar de la clase creadora; vamos a crear una jerarquía de clases como clase padre el creador que puede tener subclases muy similares entre sí.

En el anterior caso de la clase para crear una habitación, para diferenciarlo del caso que veremos a continuación, teníamos dos tipos de subproductos en cada subclase creadora: sillas y proyector. Lo más normal es que para aplicar el factory method tengamos sólo 1 tipo de producto, ya que si hay varios tipos de producto que deben trabajar siempre de forma conjunta el patrón evolucionará a un abstract factory pattern.

Así que, para comenzar, vamos a mostrar un ejemplo muy simple en el que el cliente usa un método creador para obtener distintos tipos de notificadores (email y sftp).
[code] $data = array( 1,2,3,4,5 ); $typeNotifications = array( Notifier::EMAIL, Notifier::SFTP, Notifier::EMAIL ); foreach ($typeNotifications as $type) { $notifier = Notifier::getNotifier( $type, $data ); echo( $notifier->notify() . PHP_EOL ); } [/code]
El ejemplo es muy simple, pero para imaginar un caso más real las variable $data y $typeNotifications podrían ser objetos con toda la información, o registros de una base de datos, o lectura de un fichero, ..., pero vamos a simplificarlo para ver con más claridad qué ocurre:

Detalles que llaman la atención:
  • El método creador es un método estático de la clase Notifier, es decir, no hay clase creadora.
  • Al método creador se le pasan parámetros.
  • En el ejemplo vemos que hay 3 tipos de notificaciones: email, sftp y otra vez email.

Tras ejecutar ese código tenemos el siguiente resultado:
[code] Notifyied by email next data: 1,2,3,4,5 Notifyied by sftp next data: 1,2,3,4,5 Notifyied by email next data: 1,2,3,4,5 [Finished in 0.1s] [/code]
Se observa que hemos obtenido en total dos notificadores tipo email y uno tipo sftp, tal como hemos indicado en la variable $typeNotifications. El polimorfismo entra en acción.

Entonces, ¿Dónde está la clase creadora concreta que nos devuelve el producto concreto que tiene asociado? No hay.

Veamos la clase Notifier:
[code] abstract class Notifier { const EMAIL = "email"; const SFTP = "sftp"; protected $data = array(); final private function __construct( array $data ) { $this->data = $data; } public static function getNotifier( string $type, array $data ) : Notifier { switch ( trim( $type ) ) { case self::EMAIL: return new EMailNotifier($data); break; case self::SFTP: return new SftpNotifier($data); break; default: throw new \Exception("Error: Notifier ".$type." is not available"); break; } } public abstract function notify() : string; } [/code]
Vamos a ver los puntos más importantes de esta clase:
  • Es una clase abstracta, por tanto no puede ser instaciada: no habrá objetos de la clase Notifier.
  • El método getNotifier() es estático, se puede llamar sin tener un objeto de la clase. Este es el método creador que, como se ve, devuelve una clase concreta de Notifier.
  • El constructor es privado. De esta forma una clase cliente sólo puede obtener un tipo Notifier a través del método creador.
  • El constructor es final. Con esto evitamos que las subclases puedan sobrescribir el constructor. Volvemos a reforzar que las subclases sólo se puedan obtener a través del método creador.

Los más destacado es ver que el método creador se ha movido a la clase abstracta del producto.

Con esto ya sólo necesitamos las subclases de Notifier.

Clase EmailNotifier:
[code] class EmailNotifier extends Notifier { /** * Notify */ public function notify() : string { return "Notifyied by email next data: ".implode(",", $this->data); } } [/code]
Clase SftpNotifier:
[code] class SftpNotifier extends Notifier { /** * Notify */ public function notify() : string { return "Notifyied by sftp next data: ".implode(",", $this->data); } } [/code]
Con esta variante del patrón factory method no es necesario crear una estructura jerárquica de creadores ni necesitamos que nuestra clase cliente herede de la clase creadora.

El factory method pattern, tal y como lo vemos en este ejemplo, gana mucho aplicándolo junto a principios SOLID como la inyección de dependencias.

El código de github aquí.

No hay comentarios:

Publicar un comentario