15 de abril de 2015

Patrón Factory PHP

El patrón Factory permita la instancia de objetos en tiempo de ejecución, la ventaja de utilizar este patrón de diseño es que tenemos centralizada la creación de los objetos en una clase que a pedido, nos devuelve instancias de los mismos, permitiendo tener el control de la creación de objetos.

Este modelo nos aporta una mayor claridad a la hora de programar, puede que para pequeños proyectos no sea muy útil pero en el caso de los grandes o donde trabajen varias personas puede ser muy socorrido.

Como ventajas podemos citar la centralización de creación de objetos, todo lo haríamos a través de una sola clase por lo que podríamos definir los mismos métodos y acciones para todos los objetos de una aplicación.

Primera aproximación: Familia de Clases Independientes

Se busca crear un nuevo objeto de una clase, en este ejemplo al instanciar una clase se podrán utilizar los métodos y variables que esta contenga (siempre y cuando sean public o protected), lo interesante aquí es que cada clase es independiente de otra, sin importar que pertenezcan a una misma familia de clases, en otras palabras: si bien tienen aspectos en común, no tienen un padre que les oriente en su comportamiento sino que cada clase define su comportamiento.

La ventaja de esta primera aproximación es que se conoce de los atributos y comportamiento de las clases que se instancian al estudiar cada clase independiente pues están tienen detallado en su código la función que realizan y no se debe buscar una clase padre cuyos métodos puedan estar inaccesibles debido a niveles de protección de documentos. Hago la diferencia pues en este ejemplo se utilizaran dos clases que tienen en común que realizan y entregan una conexión a una base de datos específica, sin embargo no presentan herencia de una clase abstracta que les indique los parámetros estáticos o las funciones establecidas que pueden utilizar o bien las funciones abstractas que deben definir, Los pasos para utilizar el patrón Factory son:

1. Crear o especificar en una familia las clases que se desean instanciar desde un punto único, por lo general son clases que tengan algo en común a fin de lograr una mejor optimización.

2. Importar la(s) case(s) hacia una “Clase Factory” que será la encargada de crear las instancias de las clases importadas a través de un método estático que se puede instanciar desde fuera del objeto. Este método tiene que recibir el nombre de la clase a instanciar.

3. Comprobamos que la clase solicitada exista(if(class_exists($type))) en caso de existir se devuelve una instancia de dicha clase Ejemplo:

<?php
// Clase para la utilización de base de datos MySQL

class MySQL{
 public function __construct()
 {
  echo "
Constructor MySQL";
 }

// Funcion para conectar con la base de datos...
 public function connect()
 {
  //$link=mysql_connect(...);
  echo "
Conectar con MySQL";
 }
}

// Clase para la utilización de base de datos PostgreSQL
class PostgreSQL{
 public function __construct()
 {
  echo "
Constructor PostgreSQL";
 }

 // Funcion para conectar con la base de datos
 public function connect()
 {
  //link=pg_connect(...);
  echo "
Conectar con PostgreSQL";
 }
}

class database{

 //Funcion "static" que se puede instanciar desde fuera del objeto.
 // Tiene que recibir el nombre de la clase a instanciar
 
  public static function factory($type){
  if(class_exists($type))
  {
   //Devolvemos una instancia de la clase
   return new $type();
  }else
   echo "
Error. la case '".$type."' no existe";
  return 0;
 }
}

Require_once(“database.php”)
Class prueba{

//Realizamos una instancia para el tipo de base de datos MySQL
$obj1=database::factory("MySQL");
$obj1->connect();

//Realizamos una instancia para el tipo de base de datos PostgreSQL
$obj2=database::factory("PostgreSQL");
$obj2->connect();
}
?>

Segunda aproximación: Familia de Clases Dependientes

En esta aproximación se busca crear un nuevo objeto de una sub clase, por lo que en este ejemplo se define una clase abstracta que hace el papel de padre orientando el comportamiento de las clases hijas, la desventaja de esta segunda aproximación es que puede darse el caso en que se instancien clases hijas y no se tenga conocimiento de los métodos definidos de la clase padre.

Cabe resaltar lo anterior pues en ciertos escenarios o ciertos “programadores” toman a la clase padre (abstracta o no) que define el comportamiento de las clases hijas como el pensamiento supremo y mantienen esta clase en una carpeta con niveles de protección a fin que no sea accesible para otros programadores…claro que su razón tienen y “el gran inconveniente” que te expongo se soluciona con una buena documentación.

Los pasos para utilizar el patrón Factory son:
1. Crear una clase abstracta la cual por definición nunca será instanciada, esta clase estará fuera del alcance del usuario y siendo con métodos definidos o abstractas definirán el comportamiento de las clases hijas que la hereden.

2. Implícito: Los métodos de la clase abstracta que serán utilizados por las clases hijas deben ser declarados como public fin de que sean accesibles primero por las clases hijas y luego por la clase que haga uso de la “Clase Factory”.

3. Crear o especificar en una familia las clases que se desean instanciar desde un punto único, por lo general son clases que tengan algo en común a fin de lograr una mejor optimización.

4. Importar la(s) clase(s) hacia una “Clase Factory” que será la encargada de crear las instancias de las clases importadas a través de un método estático que se puede instanciar desde fuera del objeto…tener en cuenta el magic method __autoload(). Este método tiene que recibir el nombre de la clase a instanciar

5. Comprobamos que la clase solicitada exista(if(class_exists($type))) en caso de existir se devuelve una instancia de dicha clase
<?php 

// Solamente apuntar que las clases pueden ubicarse
// en distintos directorios

abstract class HtmlComponent{
        protected $_html;
        public function getHtml(){
                return $this->_html;
        }
}

class ImageHtmlComponent extends HtmlComponent{
        public function __construct(){
  $this->_html = 'Factory Design';
  }
}
  
class InputHtmlComponent extends HtmlComponent{
  public function __construct(){       
    $this->_html = '';
       }
}

// Llamamos a las clases hijas con un require o 
// bien con __autoload()
// nunca invocar la clase abstracta

class HtmlComponentFactory{
        public static function createHtmlComponent($type){
                $baseclass = 'HtmlComponent';
                $targetclass = ucfirst($type).$baseclass;
                if(class_exists($targetclass) && is_subclass_of($targetclass, $baseclass)){
                        return new $targetclass;
                }
                else{
                        die("El tipo '$type' no existe");                 
                }
        }
}

$components = array('Image', 'Input');
foreach($components as $component){
        echo HtmlComponentFactory::createHtmlComponent($component)->getHtml();
}
?>

Tercera aproximación: Clase Factory Abstracta

Esta aproximación es una extensión de la primera aproximación: Familia de clases independientes, aquí las clases están debidamente definidas y tiene un comportamiento común lo que permite agruparlas en una familia, pero es una clase abstracta a través de un método estático quien se encargara de la creación de las instancias.

La característica de esta aproximación es que la clase que quiera utilizar la “Clase Factory” deberá heredar esta clase haciendo uso de extends, además recordemos que una clase abstracta define modelos abstractos que deberán ser implementados por aquellas clases que la hereden, por lo que debe utilizarse esta aproximación para extender el comportamiento de las clases definidas independientemente.

Basándonos en Wikipedia: Factory Method consiste en utilizar una clase constructora (al estilo del Abstract Factory) abstracta con unos cuantos métodos definidos y otro(s) abstracto(s): el dedicado a la construcción de objetos de un subtipo de un tipo determinado. Es una simplificación del Abstract Factory, en la que la clase abstracta tiene métodos concretos que usan algunos de los abstractos; según usemos una u otra hija de esta clase abstracta, tendremos uno u otro comportamiento.

He aquí un ejemplo con varias clases para conectarse a diferentes base de datos, sin importar donde desee conectarme concentraré la instancia en una clase llamada BaseDeDatos

<?php
 
class MySQL{
  public function __construct(){
    echo "Instancio MySQL";
  }
}
 
class PostgreSQL{
  public function __construct(){
    echo "Instancio PostgreSQL";
  }
}
 
// Clase abstracta con método estatico publico encargado
// de crear y entregar las instancias que fabrica

abstract class BaseDeDatos{
    public static function cargar($tipo){
        try {
            if (class_exists($tipo)) {
                return new $tipo;
            } else {
                throw new Exception("No existe la clase '$tipo'");
            }
        } catch (Exception $e) {
            echo 'Excepción capturada: ',  $e->getMessage(), "\n";
        }
    }
}
 
BaseDeDatos::cargar("MySQL");
echo "
";
BaseDeDatos::cargar("PostgreSQL");
echo "
";
BaseDeDatos::cargar("Oracle");
?>

Función útil: __autoload() __autoload funciona de la siguiente manera: Cuando se necesita una clase (haciendo un new, en un extends, un implements, etc…) si la clase no está cargada se llama a la función mágica __autoload con el nombre de la clase como parámetro, puede serte útil al implementar el patrón Factory.

<?php

function __autoload($fileName){
    $file = 'clases/' . $fileName.'.php';
    if (file_exists($file)){
        require_once $file;
    }
}
 
$nuevo = new nuevo;
?>