Patrones de diseño para torpes - 2ª parte

Tags: , , , ,

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’.
~ $

Comentarios

Patrones de diseño para torpes - 1ª parte

Tags: , , , ,

Hoy hablamos de los famosos patrones de diseño o design patterns. Hay montones de libros y artículos sobre el tema, empezando por el famoso libro “Desing Patterns“, de Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides.

El caso es que no me acabo de enterar muy bien (a ratos soy un poco lerdo) y me pierdo un poco, así que he decidido explicarme a mí mismo algunos patrones de diseño típicos.

Una de las cosas que no me gustan es que las explicaciones y ejemplos que se proponen en muchos libros o artículos están relacionadas con objetos “sofware”. ¿No es acaso la programación orientada a objetos una ayuda para representar o modelizar sistemas u objetos “reales”?

Pues voy a tratar de explicar los patrones de diseño con ejemplos cotidianos, no “software”, a ver qué tal me sale.

El patrón “Factory”

Este patrón de diseño es muy útil cuando tenemos que crear (=instanciar) distintos objetos que tienen algo en común (=comparten una interfaz). Ya me he liado con tecnicismos.

Imaginemos que vamos con nuestra cámara de fotos y el coche teledirijido de nuestr@ hij@ a una tienda de electricidad para comprar pilas. El enfoque “sencillo” (=aplicando un patrón de diseño) es decirle al vendedor: “quiero pilas para estos cacharros”. El enfoque complicado (=instanciar cada objeto por separado) es decirl: “quiero pilas AAA (de 1,5 V) para la cámara, una pila de petaca de 9V para el mando y pilas AA para el cochecito”.

Analicemos el objeto demandado en cuestión: las pilas. ¿Qué caracteriza a una pila? Su voltaje, su tamaño y poco más. Es decir, las pilas tienen una interfaz común, pero son diferentes unas de otras. Si tuviésemos que modelizarlas usando clases tendríamos la clase PilaAAA, PilaPetaca, PilaAA y alguna más.

mi_pilaAAA = new PilaAAA()
mi_pilaPetaca = new PilaPetaca()
mi_pilaAA = new PilaAA()

Cada uno de estos objetos instanciados deberíamos asociarlo a su receptor:

camara_fotos.setPila(mi_pilaAAA)
mando_distancia.setPila(mi_pilaPetaca)
cochecito.setPila(mi_pilaAA)

¿No sería más cómodo tener algo (=el dependiente) que se encargase de darnos las pilas con sólo decirle para qué las queremos?

dependiente = new Dependiente()
camara_fotos.setPila(dependiente.damePilas(this))
mando_distancia.setPila(dependiente.damePilas(this))
cochecito.setPila(dependiente.damePilas(this))

Parece un gran avance, ¿no? Cada objeto cliente (la cámara, el mando a distancia y el cochecito) se despreocupan de saber qué características tienen las pilas que usan. Simplemente le piden pilas al dependiente y listo.
Es tarea del dependiente saber qué tipo de pilas tiene que devolver, y la forma de hacerlo (=implementación) es transparente para el cliente. Hoy puede que el dependiente mire un catálogo, mañana puede que consulte una base de datos y pasado mañana a lo mejor solicita un fichero XML de aparatos y pilas, pero esto no es algo que deba saber el objeto cliente.

Bien, pues simplificando bastante, el patrón “Factory” es esto mismo. Un objeto especializado se encarga de instanciar objetos para clientes, y estos utilizan los métodos que proporciona la interfaz del objeto recibido, pero sin saber nada de las interioridades del objeto, ni siquiera qué tipo de objeto es (=a partir de qué clase está instanciado).

Veamos un ejemplo un poco más “software”. Imaginemos que tenemos una clase sencilla en una aplicación de blogs que se dedica a “pintar” en pantalla el título, autor y fecha de publicación de algunos envíos.
Estos envíos pueden estar guardados en una base de datos o provenir de un RSS, por ejemplo.

A la clase cliente (la que “pinta” en pantalla”) le da igual el origen de los datos, sólo quiere los datos. Voy a poner un poco de código –utilizaré Python, que me gusta bastante ;-) y me parece muy claro.

class EnviosRSS:
    def __init__(self, url_rss):
        # Hacemos algo para traer los datos de la url
        self.post = lo_que_sea
    def dameAutor(self):
        return self.post.autor
    def dameTitulo(self):
       return self.post.titulo
    def dameFecha(self):
        return self.post.fecha

class EnviosBBDD:
    def __init__(self):
        # Hacemos algo para traer los datos de la base de datos
        self.post = lo_que_sea
    def dameAutor(self):
        return self.post.autor
   def dameTitulo(self):
        return self.post.titulo
    def dameFecha(self):
        return self.post.fecha

class EnviosFactory:
    def dameEnvios(self, url=''):
        if url == '':
           return EnviosBBDD()
        else:
           return EnviosRSS(url)
# código cliente
factory = EnviosFactory()
envios1 = factory.dameEnvios()
envios2 = factory.dameEnvios('http://davidasorey.net/index.php/feed/rss')
# pintamos los envios
print envios1.dameTitulo()
print envios1.dameAutor()
print envios1.dameFecha()
print envios2.dameTitulo()
print envios2.dameAutor()
print envios2.dameFecha()

Unas notas sobre la terminología “pythonera”: los métodos se definen con def, las clases se instancian llamando al nombre de la clase, sin new. El constructor (algo parecido, realmente) es el método __init__. Por últimos, los métodos al definirse siempre deben llevar el self por delante. Realmente Python no es un lenguaje orientado a objetos puro, pero ni falta que le hace ;-)

Para finalizar, muy importante: esto no es una lección magistral. Es mi visión personal del patrón “Factory”, que puede ser errónea e incompleta. Se admiten (se ruegan) sugerencias y comentarios.

Comentarios (2)

Comparativa de “Frameworks” para PHP

Tags: , ,

Volvemos con la palabreja de moda: framework. Tras el enorme éxito de Ruby On Rails y otros framework como Trails, Django o TurboGears, el mundillo PHP se ha puesto las pilas y han surgido algunos proyectos similares.

¿Por qué utilizar PHP como lenguaje base para un framework en vez de otros lenguajes con una orientación a objetos más definida o clara como Ruby, Python o Java? [1]

La razón principal, en muchos casos, es la amplia extensión de PHP en los servidores. Prácticamente todos los proveedores suministran planes de hosting con PHP y MySQL. Si no se dispone de un servidor dedicado o especializado, no es habitual poder contar con Ruby, Java o Python para hacer desarrollos.

En mi opinión, los frameworks para PHP con más aceptación son el Zend Framework, CakePHP y Symfony. Mi impresión es que Zend Framework es, sobre todo, una colección de clases, paquetes y herramientas, mientras que CakePHP y Symfony son más del estilo RoR.

Entre CakePHP y Symfony se puede decir que el primero es más sencillo y que el segundo ofrece más características. Sinceramente, la mejor forma de evaluarlos es dedicando unas horas a cada uno, pero no siempre tenemos tiempo para esto. Afortunadamente, he encontrado dos comparativas (en inglés) muy ilustrativas:

[1] PHP 5 ha mejorado mucho en lo que a programación orientada a objetos se refiere. Ya tenemos control de acceso en los métodos, gestión de excepciones, …

Comentarios

La pesadilla de los “encoding” (2ª parte)

Tags: ,

Decíamos en la anterior entrada que cuando un fichero puede codificarse con UTF-8. Existen dos variantes del formato UTF-8: con BOM y sin BOM. BOM son dos bytes (FE FF) al principio de un fichero que, simplificando mucho, indican el orden de los bytes.
Guárdese un fichero codificado en UTF-8 con el BOM y tendrá problemas con la mayoría de sus scripts en PHP o ficheros XML, ya que el fichero realmente no comenzará como “<?php” o “<?xml“.

Imaginen ahora el siguiente entorno de trabajo: un servidor web más o menos moderno que sirve los documentos por defecto con la codificación UTF-8, un desarrollador con una estación de trabajo Windows o Linux y un diseñador con la estación de trabajo Mac. Si el desarrollador y el diseñador no vigilan el “encoding” con el que guardan sus ficheros habrá problemas con seguridad. Muchas herramientas de desarrollo y/o diseño Web guardan por defecto los ficheros codificados con ISO-8859-1, otras lo guardan en UTF-8 con BOM.

Por supuesto, existen soluciones, a nivel servidor (ficheros .htaccess con la directiva AddDefaultCharset) y a nivel estación de trabajo utilizando herramientas competentes que puedan lidiar con las diferentes codificaciones.

Esta es la típica página que se ve mal porque el servidor sirve en ISO-8859-1 y el fichero está codificado como UTF-8:

Captura de un navegador mostrando caracteres UTF-8 como Latin-1

Se puede ver cómo los caracteres acentuados (ocupan 2 bytes en Unicode) son interpretados como 2 caracteres sencillos.

En el blog de Juque hay más ejemplos y se amplía más la información.

La conclusión final que obtengo es la siguiente: si no se puede garantizar una uniformidad en las plataformas de trabajo (que todo el mundo trabaje con el mismo “encoding”), lo más seguro es utilizar las entidades HTML de toda la vida (á en vez de á, ñ en vez de ñ, …)

Comentarios

La pesadilla de los “encoding”

Tags: ,

Estamos en el año 2007. Internet forma parte de las vidas de muchas personas. Pues, por increíble que parezca, algun@s todavía tenemos que lidiar con problemas cuyo origen se remonta a varias décadas atrás.

Antecedentes: en los años 60 se aprobó un código estándard para la codificación de caracteres alfanuméricos. Era una buena idea, puesto que hasta el momento, cada fabricante usaba más o menos el código que le daba la gana. Con este código, ASCII, se consiguió una cierta estandarización.

La idea era utilizar 7 bits (lo que nos da 2^7 = 128 códigos diferentes) para representar los caracteres y símbolos del lenguaje escrito. Por ejemplo, el 65 (0100 0001 en binario) representaba la letra A (en mayúscula), el 32 (0010 0000 en binario) representaba el espacio en blanco, etc.

Magnífica ocurrencia, si tu idioma es inglés. En seguida l@s europe@s nos dimos cuenta de que este sistema no era muy válido: muchos caracteres no tenían representación: eñes, cedillas (ç), vocales con diversos acentos (á, à, â, ä, …)

La solución que se encontró fue rápida: ya que un byte tiene 8 bits y el ASCII sólo utiliza 7 (el octavo bit se destinó para control de errores), se decidió utilizar ese octavo bit para ampliar el rango de caracteres disponibles (2^8 = 256). Surgía la codificación ISO-8859.

También parecía una buena idea, pero no resultó ser tan idónea: algunos idiomas necesitaban una ñ, por ejemplo, pero otros necesitaban una ç. Algunos idiomas europeos sólo necesitaban un tipo de acentos, pero otros utilizaban más. El resultado de estas necesidades diferentes fue que no se llegó a una única codificación utilizando 8 bits. La codificación ISO-8859-1 (también llamada Latin-1) sirve para casi todos los idiomas de Europa Occidental, la ISO-8859-15 es una revisión de ésta que incluye el símbolo del euro (€). La codificación ISO-8859-16 (Latin-2) se adapta a idiomas de Europa Oriental.

Por otra parte, otros fabricantes decidieron hacer lo que les pareció con el 8º bit no utilizado. Añadieron nuevos símbolos o caracteres extraños al ASCII estándar. Surgen así diversos “codepages” parcialmente compatibles entre ellos (CP437, Windows-1252, …) y con ISO-8859

Estas codificaciones extendidas supusieron una mejora, pero no fueron el remedio definitivo. Además de mantenerse ciertas incompatibilidades, no servían para codificar algunos idiomas no alfabéticos (idiomas fonéticos o ideográficos).

La siguiente idea fue Unicode: utilizar 2 byte completos (o más) para representar los caracteres o símbolos. Con 2 bytes (2^16) ya tenemos 65.536 posibilidades o símbolos diferentes. Unicode consigue representar y asignar un código único a prácticamente cualquier símbolo escrito en cualquier idioma humano. Además, mantiene la compatibilidad con ASCII y Latin-1: los 128 primeros caracteres de Unicode se corresponden con la codificación ASCII, y los 128 siguientes (hasta 256) se corresponden con Latin-1.

El problema de Unicode es la transmisión de los datos: es absurdo reservar siempre dos bytes para un carácter cuando la mayor parte de los caracteres Unicode que se pueden transmitir en un momento dado “caben” en un byte. Las diferentes formas de transmitir e interpretar los caracteres Unicode son los famosos UTF, siendo los más utilizados UTF-8 y UTF-16.

Seguiremos con el tema.

Comentarios

<- artículos más recientes      artículos anteriores ->

896 comentarios de spam
bloqueados por
Akismet