miércoles, 28 de noviembre de 2007

Patrones de diseño para torpes - 2ª parte

Seguimos la serie de artículos sobre patrones de diseño. Vamos a ver otro de los patrones más típicos.

El patrón "Observer"


Usted es el/la encargado/a de la hemeroteca de un laboratorio. Periódicamente se reciben revistas sobre diferentes temas y con una periodicidad variable: unas son semanales, otras quincenales y otras mensuales.

Una de sus funciones es hacer llegar las revistas a diferentes personas de diferentes departamentos. Por ejemplo, la revista "Genómica de las hormigas" debe llegar al laboratorio de genética, la revista "Cristalografía de minerales venusianos" va al departamento de cristalografía, etc.

Imagínese ahora que cada persona de cada departamento estuviese cada dos por tres llamándole para preguntarle si ha llegado su revista, o, peor aún, entrara en su despacho y revolviese su mesa y el archivo buscando su preciada revista.

Obviamente, las personas no trabajamos así, en este ejemplo, lo más lógico, cómodo y organizado es que Ud. mantenga una lista de las personas o departamentos y notifique a estos la llegada de las revistas. Cada departamento decide entonces qué hacer con la revista (fotocopiarla, escanearla, ..., incluso leerla)

Cuando desarrollamos programas hay distintos módulos o unidades funcionales que deben interaccionar entre ellos, pero esta interacción no siempre es tan limpia o aséptica como sería deseable. En ocasiones un módulo, clase, función (o lo que sea) llega a "meterse en el despacho" de otra unidad funcional.

Pongamos un ejemplo más "software". En el libro "Desing Patterns" que mencionábamos en el anterior artículo proponen dos ejemplos muy ilustrativos:

  • Una hoja de cálculo puede tener varias vistas (formas de presentar los datos) activas en un momento dado: un gráfico de barras, una rejilla de datos, un gráfico por sectores, ...
    Si los datos cambian, todas las vistas deben actualizarse. La forma más cómoda es tener un objeto "Datos" que informa a todos los objetos "Presentación" de los cambios que se producen. Estos objetos "Presentación" ya se ocuparán ellos de actualizarse.
    Lo bueno de este patrón es que los objetos "Presentación" no deben "meterse en el despacho" del objeto "Datos" para consultar si estos datos han cambiado o no.

  • El otro ejemplo que se propone es un reloj. Tenemos un objeto "Temporizador" que cada segundo notifica a todos los objetos que le observan (un "RelojDigital", un "RelojAnalógico", ...) de que algo ha cambiado.


Distinguimos entonces el objeto observado y los objetos observadores. El objeto observado debe mantener una relación de los objetos que le observan y una forma de notificarles. Por su parte, los objetos observadores, sean del tipo que sean, deben implementar alguna funcionalidad para actualizarse en función del nuevo estado del objeto observado.

En el artículo "Five common PHP design patterns" nos proponen algunos ejemplos en PHP muy interesantes. Vamos a adaptar un poco el ejemplo correspondiente al patrón observer y "traducirlo".

<?php

interface IntfzObservador {
function hayCambios($observado, $datos);
}

class NotificadorUsuarios implements IntfzObservador {
public function hayCambios($sender, $args) {
// Código para notificar: mensaje en consola,
// registro en un fichero 'log', etc.
echo("Se ha añadido '$args' a la lista de usuarios.n");
}
}

class GrabadorBBDDUsuarios implements IntfzObservador {
public function hayCambios($sender, $args) {
// Código para grabar en BBDD.
echo("Se ha grabado el usuario '$args' en la base de datos.n");
}
}

class AltaSistemaUsuarios implements IntfzObservador {
public function hayCambios($sender, $args) {
// Código para crear la cuenta de usuario.
echo("Se ha creado una cuenta para el usuario '$args'.n");
}
}

class ListaUsuarios {
private $observadores = array();
public function anadirUsuario($nombre) {
foreach($this->observadores as $obs)
$obs->hayCambios($this, $nombre);
}
public function anadirObservador($observador) {
$this->observadores[]= $observador;
}
}

/** Código de pruebas **/
$nu = new NotificadorUsuarios();
$gbu = new GrabadorBBDDUsuarios();
$asu = new AltaSistemaUsuarios();
$lu = new ListaUsuarios();
$lu->anadirObservador($nu);
$lu->anadirObservador($gbu);
$lu->anadirObservador($asu);
$lu->anadirUsuario("Juana");
$lu->anadirUsuario("Pedro");

?>

Las clases observadoras, independientemente de su naturaleza, deben tener un método común para ser notificadas por el observado. Esto puede garantizarse definiendo una interfaz (IntfzObservador en el ejemplo) y forzando a que estas clases observadoras implementen la interfaz.

El código es autoexplicativo. Tenemos dos observadores que "reaccionan" de diferente forma a los cambios en el observado. Uno de ellos de dedica a notificar los usuarios nuevos, otro los graba en una base de datos, otro los da de alta en el sistema, ...

Cada vez que se añade el objeto ListaUsuarios añade un usuario, notifica a sus observadores y éstos hacen sus tareas, sin interferir con este objeto observado.

Veamos la salida de este programa:

~ $ php test.php
Se ha añadido 'Juana' a la lista de usuarios.
Se ha grabado el usuario 'Juana' en la base de datos.
Se ha creado una cuenta para el usuario 'Juana'.
Se ha añadido 'Pedro' a la lista de usuarios.
Se ha grabado el usuario 'Pedro' en la base de datos.
Se ha creado una cuenta para el usuario 'Pedro'.
~ $

viernes, 16 de noviembre de 2007

Virtualización en Mac OSX (2)

En otra entrada escribía sobre Qemu y el "front-end" para Mac OSX, "Q". He seguido buscando otras soluciones y he encontrado otro programa de virtualización: Virtual Box.

Me recuerda mucho a VMWare Workstation y me parece más completo que el tándem Q-Qemu. Tiene un asistente de instalación y configuración de nuevas máquinas virtuales, permite usar puertos serie y USB, audio y varias interfaces de red, ...

También provee de un conjunto de drivers para el sistema virtualizado (Windows o Linux) que mejoran las prestaciones de la máquina virtual (resolución de la pantalla adaptable, directorios compartidos con la máquina anfitriona, ...)

Resumiendo: me ha gustado mucho esta herramienta. Las máquinas virtualizadas parece que van más rápido que con Qemu.

Pantallazo 1: Mac OSX como anfitrión, ejecutando dos máquinas virtuales (Windows XP y Ubuntu Linux). Las tres máquinas están mostrando un "Hola, mundo" escrito en Python.

Mac OSX como anfitrión, Windows XP y Ubuntu virtualizados

Pantallazo 2: Windows XP como anfitrión, máquina virtual: Debian Etch

Windows XP como anfitrión, máquina virtual: Debian Etch

domingo, 4 de noviembre de 2007

Colores

colores.pngEn el colegio nos enseñaban que los colores primarios eran amarillo, rojo y azul, que mezclando azul y amarillo salía color verde ...
Luego empezamos a trabajar con pantallas y nos encontramos con que los colores "primarios" son los del sistema RGB (rojo, verde y azul) y que rojo y verde da amarillo.
Cualquiera que sepa algo de diseño sabe que son dos sistemas de color diferentes, el aditivo y el sustractivo, y que los colores primarios de cada sistema no son los mismos, aunque coloquialmente se les llame igual.
Pero claro, a un pobre programador como yo, al que si le sacas de lo suyo está totalmente perdido, estas cosas se le escapan ;-)
El problema es que el "rojo" del sistema RGB (#FF0000) en realidad es "rojo anaranjado", el "azul" RGB (#0000FF) es "azul violáceo".
En esta figura se ve la "equivalencia" entre los dos sistemas. Las mezclas funcionan entre dos colores alternos. Por ejemplo, rojo + verde = amarillo, verde + azul = cian, ...

Para saber más, este "post" en malaciencia.info

viernes, 2 de noviembre de 2007

Tira nº 57: corte y confección




Ahora en serio: en mi blog estoy haciendo una serie "divulgativa" sobre patrones de diseño.
El primero que trato de explicar es el patron "factory".