17 de septiembre de 2009
Este es un post perteneciente a la serie "Patrones de Diseño".
Normalmente para crear un objeto de una clase se utiliza el operador "new" que nos brinda el propio lenguaje de programación, pero a veces, la creación de un nuevo objeto requiere de una serie de pasos, configuraciones e incluso puede ser necesario crear objetos de alguna subclase. Este problema se ve incrementado si el número de objetos (de la misma clase) a crear, es elevado. El problema entonces: ¿Cómo crear objetos complejos de forma fácil, rápida y sin posibilidad de errores?
La solución a este problema puede ser la siguiente: crear un método de clase (o una función) que se encargue de crear la instancia, configurar los nuevos objetos e incluso de crear objetos dependientes de otras clases. De este modo la llamada será simple, independientemente de la complejidad que tenga esa clase en si para ser instanciada.
Esta solución es la que nos brinda el patrón "Factory", y se puede ver más fácilmente con los siguientes ejemplos:
- Ejemplo 1:
$connection = new DbConnection($host, $user, $pass);
// Esta forma no sería la adecuada
// Conexion creada mediante un método estático regido por el patron "Factory",
// que aisla el código de la conexion a la base de datos
$connection = DbConnection::createConnection();
// Esta forma seria la correcta.
?>
- Ejemplo 2:
// La clase Application configura la aplicacion, en este caso se instancia
// mediante su constructor y el paso de ciertos parametros.
$application = new Application(array(
'app_name' => 'appTest',
'version' => '1.0',
'path' => '/'
));
// De esta forma la configuracion se hace a traves del metodo
// configure de una forma sencilla. configure() se rige por el patron
// Factory
$application = Application::configure();
?>
¿ Qué pasaría en caso de no utilizar el patrón Factory si es necesario cambiar la estructura de los objetos ? habría que ir objeto por objeto de la clase reformada cambiando su estructura y adaptándolo a su nueva forma, la pérdida de tiempo que esto ocasionaría se incrementa por el número de objetos que se crean en la aplicación y la complejidad de estos.
Sin embargo, si se utiliza un método regido por "Factory" para crear los objetos, la adaptación de estos a la nueva estructura vendría por la modificación única en el método que crea la instancia, con lo cual la pérdida de tiempo es mínima, sin hablar de la posibilidad de errores que se pueden cometer si el número de objetos a modificar es elevado.
El patrón "Factory" no es solo aplicable a la clase, es decir, se puede crear un método interno de una clase para que el resto de métodos de la clase puedan llamar a un código necesario en cada uno de ellos sin necesidad de repetir éste:
<?php
class DbActions{
public function list(){
$connection = $this->_getConnection();
// código a ejecutar para sacar el listado
}
public function getById($id){
$connection = $this->_getConnection();
// código a ejecutar para obtener el registro
// mediante la id pasada
}
// _getConnection aisla y devuelve la conexion
private function _getConnection(){
return new DbConnection(
DB_HOST,
DB_USER,
DB_PASS
);
}
}
?>
Una vez explicada la funcionalidad del patrón "Factory" se puede aplicar a un ejemplo más complejo:
Archivo index.php
<?php
// Comienza la ejecucion del programa:
// se instancia Application, con los datos
// necesarios para la ejecucion.
// Se hace uso de Singleton para la instancia.
require_once ('application.php');
$application = Application::getInstance();
// Ejecucion de un query.
$results = $application->db->executeQuery("SELECT * FROM `porfolio`");
?>
Archivo application.php
<?php
class Application{
/**
* $instance = Object
*/
private static $instance = null;
/**
* Contiene un objeto de la clase que conecta a la BBDD.
* $db = DbConex Class
*/
public $db = null;
/**
* private __construct
*/
private function __construct(){
// Conexion a la BBDD
require_once ('dbconex.php');
// create_connection es un metodo regido por "Factory"
// que abstrae la logica de configuracion, conexion
// y acceso a la BBDD.
// Solo requiere el motor de BBDD como parámetro
$this->db = DbConex::create_connection('MySQL');
}
/**
* Metodo que devuelve la instancia (si no existe la crea) de
* la clase Application. Se rige por "Singlenton".
*
* @return Application
*/
public function getInstance(){
if (is_null(Application::$instance)) {
Application::$instance = new Application();
}
return Application::$instance;
}
}
?>
Archivo dbconex.php
<?php
/**
* Clase padre regida por "Factory" que inicializa
* la conexion a la base de datos
*
* @return DbConex
*/
abstract class DbConex
{
/**
* datos de conexion
*/
protected $data = array(
'db_host' => 'localhost',
'db_user' => 'user_test',
'db_pass' => 'user_test'
);
/**
* Identificador de la conexion
*/
public $resource = null;
/**
* Es el metodo que instancia la subclase que va
* a conectar a la base de datos.
* @param: $type
*
* @return Object
*/
public static function create_connection($type){
switch ($type) {
case 'MySQL':
$db = new MySQLDbConex();
break;
case 'Oracle':
$db = new OracleDbConex();
case 'PostgreSQL':
$db = new PostgreSQLDbConex();
}
return $db;
}
/**
* metodo que ha de ser sobreescrito por las clases
* hijas que se va a encargar de realizar la consulta
* SQL.
* @param $sql = String
*
* @return mixed
*/
abstract public function executeQuery($sql);
}
class MySQLDbConex extends DbConex{
public function __construct(){
$this->resource = mysql_connect(
$this->data['db_host'],
$this->data['db_user'],
$this->data['db_pass']
);
mysql_select_db('aramirez', $this->resource);
}
public function executeQuery($sql){
// Codigo de ejecucion del Query SQL
}
}
class OracleDbConex extends DbConex{
public function __construct(){
// Codigo de conexxion a Oracle
}
public function executeQuery($sql){
// Codigo de ejecucion del Query SQL
}
}
class PostgreSQLDbConex extends DbConex{
public function __construct(){
// Codigo de conexxion a PostgreSQL
}
public function executeQuery($sql){
// Codigo de ejecucion del Query SQL
}
}
?>
Tags: Programación | PHP