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.