El patrón Observer y su implementación en PHP

Volvemos con el tema de los patrones de diseño y PHP, que estaba abandonado (y ahora tengo más tiempo para escribir con el parón temporal del podcast). Esta tarde vamos a hablar del patrón Observer, que en castellano es conocido como el patrón publicación-inscripción.

La idea de este patrón es que nos permita definir una dependencia uno-a-muchos entre varios objetos, provocando que cuando uno cambie de estado se lo notifique a sus objetos dependientes. El objetivo de este patrón es reducir el acoplamiento entre clases que requieren mantener una gran consistencia en sus relaciones, siendo clave para la implementación del patrón MVC. Dentro de esta relación denominamos Sujeto al objeto que emite la información sobre el cambio de estado, y Observador al que la recibe para realizar la acción que sea necesaria.

Os dejo esta imagen con un diagrama cargado desde la Wikipedia para ilustrar su funcionamiento:

Diagrama Estructura Patrón Observer
Un diagrama de estructura del patrón Observer (publicación-inscripción).

En PHP 5, a partir de la revisión 5.1 contamos con las interfaces SplSubject y SplObserver para implementar respectivamente el sujeto y el observador.

Vamos con un ejemplo clásico: una tienda on-line que notifica la compra y la venta de un artículo.

<?php
class Articulo implements \SplSubject /*Empezamos importando la interfaz del sujeto*/
{
    protected $storage;

    public function __construct(\SplObjectStorage $storage)
    {
        $this->storage = $storage;
    }

    public function compra()
    {
        // compra
        $this->notify('comprado');
    }

    public function vende()
    {
        // vende
        $this->notify('vendido');
    }

    public function attach(\SplObserver $observer)
    {
        $this->storage->attach($observer);
    }

    public function detach(\SplObserver $observer)
    {
        $this->storage->detach($observer);
    }

    public function notify($event = '')
    {
        foreach ($this->storage as $observer)
            $observer->update($this, $event);
    }
}

class Notify implements \SplObserver /*seguimos con lai implementacion del observer*/
{
    public function update(\SplSubject $subject, $event = '')
    {
        if ($event == 'comprado')
            echo 'El artículo se ha comprado' . PHP_EOL;
        else if ($event == 'vendido')
            echo 'El artículo se ha vendido' . PHP_EOL;
    }
}

/*¿Cómo luciría llevado a la práctica?*/

/*Creamos el objeto*/
$articulo = new Articulo(new \SplObjectStorage());
/*vinculamos el artículo al observer*/
$articulo->attach(new Notify()); 
/*imprimiría en pantalla que se ha comprado*/
$articulo->compra();
/*imprimiría que se ha vendido*/ 
$articulo->vende(); 
?>

Es una idea básica donde simplemente se imprime en pantalla el resultado de una acción, pero ya os hacéis una idea de las posibilidades de este patrón.

Anuncios

El patrón Registry (y su aplicación en PHP)

Volvemos a los patrones de diseño tras un par de artículos de otra índole, y lo hacemos con Registry. La idea de este patrón es que nos permita utilizar los objetos y variables registrados en cualquier nivel de la aplicación sin necesidad de duplicarlos. Es decir, en la práctica nos permitiría usar cualquier objeto como si fuera un Singleton.

La idea de este patrón es crear una clase que tenga un array donde guardar los objetos registrados y una serie de métodos: uno que añada los objetos al array, otro que compruebe su existencia para evitar duplicaciones, otro que devuelva el objeto pasándole su clave, uno que borre el objeto recibiendo la clave y finalmente uno que limpie el array de la clase.

Pasado a código, PHP como siempre, la estructura sería la siguiente:

    <?php      
    class Registry  
    {  
        /** 
         * Registra variables y objetos         
         */  
        static private $registry = array();  
      
        /** 
         * Método que añade objetos
         * Recibe el objeto (por referencia) y la clave
         * Devuelve un booleano para confirmar si se ha insertado
         * o si en cambio estaba duplicado.
         */  
        static public function add($key, &$elemento)  
        {  
            if (!self::exists($key)) {  
                self::$registry[$key] = $elemento;  
                return true;  
            } else {  
                return false;  
            }  
        }  
      
        /** 
         * Función que comprueba la existencia de una clave.
         * Devuelve un booleano confirmando si existe o no.
         */  
        static public function exists($key)  
        {  
            return array_key_exists($key, self::$registry);  
        }  
      
        /** 
         * Función que devuelve un item dada la clave          
         */  
        static public function get($key)  
        {  
            if (self::exists($key)) {  
                return self::$registry[$key];  
            } else {  
                return null;  
            }  
        }  
      
        /** 
         * Elimina una entrada recibiendo su clave y devuelve confirmación.
         * Si la clave no existe devuelve false.
         */  
        static public function remove($name)  
        {  
            if (self::exists($name)) {  
                unset(self::$registry[$name]);
                return true;  
            } else {
                return false;
            }  
            
        }  
      
        /** 
         * Limpia el registro totalmente.         
         */  
        static public function clear()  
        {  
            self::$registry = array();  
        }  
    }  
    ?>  

El ejemplo es una estructura muy básica, puedes ampliarlo con filtros varios, control de errores y demás, pero la idea del esqueleto de este tipo de clase.

Ale, a disfrutarlo.

Patrón MVC y PHP

Cierto es que este mes he estado poco activo en cuestiones bloggeras, pero ya me tenéis de vuelta con el tema de los patrones de Diseño, en este caso con el popular MVC (Model/View/Controller o Modelo/Vista/Controlador).

La idea del patrón MVC es separar en tres capas los datos (model), la interfaz de usuario (view) y la lógica de negocio (controller). Esto permite una mayor reutilización de componentes, facilita la modificaciones del aspecto del programa/web sin tener que tocar el funcionamiento y simplifica el mantenimiento. Funciona de forma “triangular”, es decir, cada capa envía información a una y la reciba de la otra.

El modelo sería la representación de la información y se encargaría de gestionar el acceso, adición y modificación de los mismos (operaciones CRUD). A nivel de interacción con los otros componentes enviaría a la vista los datos que tienen que ser mostrados y recibiría desde el controlador las peticiones de acceso y manipulación.

La vista sería, como su nombre indica, lo que se ve en pantalla. Además de mostrar los datos al usuario le permite interactuar con ellos. La vista recibe del modelo los datos a mostrar y permite al usuario comunicarse con el controlador a través de sus objetos.

Finalmente el controlador, trabajaría como puente entre la vista y el modelo, recibiendo las peticiones del usuario a través de la primera, realizando las labores de transformación necesarias y realizando finalmente las peticiones al modelo.

Aunque el MVC nació originalmente (allá por los años 70) para aplicaciones de escritorio, el desarrollo de la web dinámica hizo que fuera adoptado como patrón para diseñar la arquitectura de muchos sitios web. Existen múltiples frameworks para diversos lenguajes que permiten implementar el MVC a la arquitectura web. En el caso de PHP hay gran variedad, pero desde aquí vamos a destacar cuatro de ellos, todos software libre:

  • Zend Framework: Dado que Zend es la empresa con más peso en el mundo de PHP, comenzaremos con su framework. He de decir que hasta la versión 1.9 existen muchas quejas al respecto de su rendimiento, pero a partir de la versión 2 se reescribió el código para lograr aligerarlo. Está bajo licencia BSD, por lo que su uso es cercano al dominio público.
  • Symfony: Creo que a día de hoy es el framework más utilizado en PHP. Funcional, ligero, productivo y bien estructurado, es utilizado por gran número de CMS  (destacando Drupal) y por empresas potentes. Está bajo licencia MIT.
  • Codeigniter: Un veterano, al menos según mi percepción porque fue del primero del que oí hablar. Por lo que se dice tiene menos funcionalidades que Symfony o Zend, pero a su favor juega con tener una mayor flexibilidad y menor peso. Menos productivo pero más adaptable. Su licencia es GNU/GPL.
  • CakePHP: Irrumpió con mucha fuerza hace un par de años, pero parece que al final no ha logrado desbancar a Zend y Symfony del trono, en parte porque se dice que tiene una curva de aprendizaje más pronunciada. Incluye funcionalidades para casi todo y está bajo licencia MIT al igual que Symfony.

Y con estos cuatro no se acaba, hay docenas de frameworks para PHP funcionales como Yii, ZanPHP (de esto no tengo muy claro su estado, porque la web oficial a día de escribir este artículo, aparecía como un dominio caducado, pero en GitHub sigue) o KumbiaPHP. En cualquier caso, la oferta es amplia y variada en ese sentido.

El patrón de diseño Singleton (implementado en PHP)

Vamos con la entrada número 400 del blog, y la 200 de informática. Curioso, llegados a este punto de casi dos años bloggeando un tema que no era el principal ha acabado ocupando el 50% del contenido del blog.

En fin, seguimos con los patrones de diseño. El otro día hablaba del patrón Decorator, hoy vamos con el Singleton.

La idea del patrón Singleton, también llamado de instancia única, es restringir la creación de objetos pertenecientes a una clase a sólo uno. Garantiza que sólo hay una instancia y proporciona un único punto de acceso.

La idea es que la clase tenga un constructor privado para que no pueda ser accedido desde fuera de la clase y que aunque haya clases hijas estas no puedan instanciar objetos. También debe tener una propiedad estática privada donde almacenaremos nuestra instancia y un método estático que sea el punto de acceso global a la misma, devolviéndola cuando se lo llama.

Vamos con el ejemplo típico y tópico del objeto de base de datos, que suele ser el uso más común de este tipo de clases. Porque crear varias instancias de acceso a una base de datos en la mayoría de los casos es un malgasto de recursos, lo mejor suele ser tener una sola instancia para realizar las conexiones.

class Database {
//Propiedad estática, inicializada a nulo, donde guardaremos la instancia de la propia clase
    static private $instance = null;
//Definimoms el método constructor como privado
    private function __contruct() {}
     
//Método estatico que sirve como punto de acceso global
    public static function getInstance() {
        if (self::$instance == null) {
//Si no hay instancia creada, lo hace. Si la hay tira p'alante
            self::$instance = new Database();
        }
//Al final devuelve la instancia
        return self::$instance;
    }
	     
 //a continuación ya meterías todas las funciones necesarias para un objeto de base de datos. CRUD, etc...
}

Y ya est… No, sorry man pero no. Con el código así todavía es posible que alguien logre hacer más de una instancia. Y te dirás ¿cómo? Pues porque todavía es posible usar la clonación y la serialización para lograrlo. Cosa mala, pero con los métodos mágicos de PHP5 podemos arreglarnos.

Para evitar la cuestión de la clonación definimos el método mágico __clone() en la clase, para que en lugar de permitir el clonado del objeto nos lance un error y detenga el script:

public function __clone()
{
  trigger_error("No puede clonar una instancia de ". get_class($this) ." class.", E_USER_ERROR );
}

Y vamos con el tema de la serialización y deserialización. Con la función serialize() se puede almacenar una representación apta de una variable (y una instancia de un objeto es, al fina, una variable) mientras que con unserialize() podemos acceder a ella como se si tratara de un clon. De nuevo hay que redefinir un método mágico, en concreto esta vez __wakeup(), que es el método que se invoca al deserializar un objeto. De esa forma se evita que pueda ser accedido. También podría hacerse con __sleep(), que es el que se invoca al serializar, pero veo más práctico evitar la deserialización.

public function __wakeup()
{
  trigger_error("No puede deserializar una instancia de ". get_class($this) ." class.", E_USER_ERROR );
}

En fin, he caído en el ejemplo tópico, pero también me pareció el más claro. Un saludo, disfruta este articulo 400.

El patrón de diseño Decorator (aplicado en PHP)

Iba a poner El patrón de diseño decorator en PHP pero dado que los patrones de diseño son independientes del lenguaje preferí poner un título más matizado. Aunque algunos parezcan empeñados en no usar patrones en PHP (a pesar de que desde PHP5 ya esté orientado a objetos) en un proyecto grande serán útiles.

El patrón Decorator se pensó para permitir herencia múltiple sin que el árbol de clases crezca de forma incontrolada. Permite añadir a un objeto funcionalidades de forma dinámica. Piensa en un juego de rol, piensa en cuando vinculabas una matriz de conjuros a un arma en Runequest… vale, es muy friki, pero lo voy a usar de ejemplo para explicar el patrón.

Imagina que haces un videojuego, inspirado en el antes citado Runequest mismamente. Entonces creas la clase espada, que hace 10 puntos de daño y tiene 20 puntos de armadura. Muy bien, ahora le vinculas un hechizo de armadura extra. Tendrías que crear la clase espada_armadura con 10 puntos más de armadura. Hasta ahí, bien: tienes la clase espada, y la clase hija espada_armadura que hereda de espada.

Ahora imagina que también le vinculas un hechizo que duplica el daño. Tendrías que crear la clase espada_daño, pero también la clase espada_armadura_daño, que sería la clase con ambos hechizos. Para dos funcionalidades has tenido que crear 3 clases hijas. Y ahora supón un tercer hechizo que mejora tu posibilidad de éxito un 100%: espada_éxito, espada_éxito_armadura, espada_éxito_daño, espada_éxito_daño_armadura… y las cuatro clases anteriores. Para tres funcionalidades ya vas en 8 clases, si haces el cálculo progresan en potencias de 2, por lo que para 4 funcionalidades tendrías 16 clases, para 5 32

El patrón Decorator nos permite evitar esto a base de delegar funciones. Vamos a poner un ejemplo práctico en PHP solucionando el problema anterior, explicado en los comentarios:


//interfaz arma, común a todas las armas
interface iArma{
  public function ataca();
  public function defiende();
}

//clase espada, que implementa la interfaz arma
class espada implements iArma{
  private $armor = 20;
  private $attack = 10;

  public function ataca(){
    return $this->attack;
  }
  public function defiende(){
    return $this->armor;
  }
}

//creamos la clase abstracta Decorator
abstract class espada_decorator implements iArma{
  protected $_espada;
  public function __construct(iArma $Espada){
    $this->_espada = $Espada;
  }
}

//ahora creamos la funcionalidad con más ataque
class extra_attack extends espada_decorator{
  public function ataca(){
    return $this->_espada->ataca()*2;
  }
}

//ahora la funcionalidad de más defensa
class extra_armor extends espada_decorator{
  public function defiende(){
    return $this->_espada->defiende()+10;
  }
}

//ahora podemos crear un objeto espada con la defensa extra
$espada = new extra_armor(new espada());

//y ahora añadimos el ataque extra
$espada = new extra_attack($espada);

Como puedes ver, de esta forma agregamos a nuestro objeto diversas funcionalidades, creando sólo una clase por funcionalidad en lugar del montón al que nos obligaría el ir creando clases para cara posible combinación.