Custom Search
Bloggers Activos
Emacs como IDE para CakePHP aarkerio
La Negación del Viaje Lunar tonathiu
Porque los mononeurones si tenemos madre! blacksoul
BrunoFerías thot
The Art vendaval
Aclimatación extraterrestre ¿para qué? ahuramazdah
¿A que le tienes miedo? teosho
Sobre nazis, terror y medios tonathiu
Amenazas a la cuarta dimensión ¿de veras? ahuramazdah
Tarjeta Broadcom BCM94311MCG rev 02 teosho
Last Download
Segunda Fundación
Segunda Fundación
Pidiendo OpenSolaris 2008.5
vendaval
Sospechosismo
aarkerio
Slackware 12.1 Final
vendaval
Jaime Maussan da por auténtico video trucado del chupacabras hecho en Blender 3D
asarch
Linux hot girl
aarkerio
Calderón puede ser sujeto a juicio político, sostiene Carrancá
tonathiu
La desnutrición en México
aarkerio
Sistema Infalible
ordbal
Histórico
aarkerio
Nietzsche en la FCPyS
aarkerio
Google Groups Karamelo
Visit this group
GNU/Linux
GNU/Linux
Hacktivismo
Hacktivismo
Debian
Debian
NetBSD
NetBSD
WWW
WWW
Guia Linux
Guia Linux
Server Side
Server Side
Ofimatica
Ofimatica
Despabilando...
Despabilando...
Mundo Maya
Mundo Maya
Literatura
Literatura
Agora
Agora
Psicologia
Psicologia
Economia
Economia
Ambientalismo
Ambientalismo
Desarrollo
Desarrollo
Biologia
Biologia
No a la crueldad
No a la crueldad

Hacktivism

LinuxChix button

Desarrollo \ Patrones de diseño en Python
Desarrollo
Patrones de diseño en Python

Este artículo ha sido consultado en 846 ocasiones.

Patrones de diseño en Python


Vespe Savikko

Software Systems Laboratory
Tampere University of Technology
FIN-33101 Tampere, Finland
vespe@cs.tut.fi

Traducido por Iván Márquez Larios
ivanjose@gmail.com


Sinopsis


La elección del lenguaje con el que se implementará afecta el uso de los patrones de diseño.  Naturalmente algunos lenguajes aplican mejor que otros para distintas tareas.  Cada lenguaje tiene su propio conjunto de fuerzas y debilidades.  En este ensayo implementamos algunos de los patrones de diseño más comunes en Python, cuyo modelo de objetos es bastante distinto al de los lenguajes orientados a objetos convencionales.  Por lo tanto, conseguimos algo de visión interna de los patrones en el contexto de Python.



1. Introducción


En la ingeniería de software el término patrón describe una solución probada a un problema común en un contexto específico.  Los patrones pueden ser divididos en 3 distintas categorías dependiendo del nivel de abstracción e independencia del lenguaje de implementación: patrones arquitectónicos, patrones de diseño e idiomas [BMR+96].  En este trabajo nos concentramos en las últimas dos categorías: los patrones de diseño como son descritos en el libro de los conocidos como GANG OF FOUR (Banda de cuatro) [GHJV95] y los idiomas del lenguaje Python.  Los patrones no son sólo modelos microarquitectónicos, también son útiles como un vocabulario de diseño para los ingenieros de software.  La arquitectura general del sistema y las decisiones de diseño relacionadas pueden ser explicadas dando un conjunto de patrones utilizado.  Aunque nuevos patrones surgen, el GOF permanece como una referencia definitiva de patrones de diseño.  Por esta razón es importante introducir estos patrones, las nociones y la teoría detrás de ellos y su aplicabilidad a la comunidad Python.

El GOF está divido en tres partes y cada una describe los patrones relacionados con el tema de esa parte.  Los temas describen el propósito de los patrones.  Los patrones de creación se relacionan con problemas de instanciación de objetos.  Los patrones estructurales se concentran en la composición de los objetos y sus relaciones con la estructura de los objetos en tiempo de ejecución.  Mientras que los patrones estructurales describen la distribución del sistema de objetos, los patrones de comportamiento se enfocan en la dinámica interna e interacción de los objetos en el sistema.

Aunque los patrones de diseño aspiran a ser independientes del lenguaje requieren - al menos implícitamente - algo de soporte desde el lenguaje de implementación y especialmente desde su modelo de objetos.  En el GOF los lenguajes elejidos son C++ y Smalltalk.  Por ello la disponibilidad de especificadores de acceso y funciones miembro estáticas (métodos de clase) son asumidos.  El blanco es mirar algunos de los patrones del GOF y tratar de implementarlos en un lenguaje (Python) cuyo modelo de objetos sea radicalmente distinto del de C++.  Al hacer esto podemos llegar a algunas conclusiones acerca de la generalidad de los patrones del GOF y sus comodidades - si las hay - son necesarias cuando los aplicamos al ambiente de programación de Python.  Y mientras operamos en el nivel del lenguaje de programación observamos algunos idiomas de Python y cómo pueden ayudar a implementar los patrones del GOF.

Comenzamos con una breve descripción de Python como un lenguaje de objetos en la sección 2 y luego avanzamos hacia los patrones seleccionados.  Elegimos un patrón de cada categoría del GOF: Singleton (creacional, sección 3), Cadena de responsabilidad (comporamiento, sección 4) y Proxy (estructural, sección 5)
En la sección 6 sumarizamos las lecciones aprendidas y analizamos a detalle la generalidad de nuestras soluciones de implementación.

2 Python

Simplificando, Python es un lenguaje interpretado con tipos de datos débiles y dinámicos.  Estos atributos combinados con una gran variedad de extensiones hacen que Python sea la elección natural para distintos propósitos, por ejemplo scripts CGI. Python también es mencionado como un fuerte candidato para un lenguaje de manejo [Dub96 ] por su naturaleza expresiva e interpretada.  Desde la perspectiva de la programación orientada a objetos, Python tiene todas las características necesarias y más: clases, herencia múltiple, sobrecarga de operadores.  Un aspecto peculiar del modelo de objetos de Python es su negligencia hacia el ocultamiento de los datos. Por el momento digamos que la política de Python puede ser descrita como ''encapsulación por cortesía '' siendo esto que la instancia de una clase asume que ninguno de sus clientes tratará de traspasar la interfaz de la clase y manipular los atributos de la instancia directamente.  Sin embargo, la situación no es totalmente severa; la versión 1.4 del lenguaje impelmenta privacidad utilizando ocultamiento de nombres.  Esta solución se considera experimental antes de concebir un mecanismo adecuado [vR96] .

La otra característica sobresaliente de los objetos de Python es que no son meramente instancias de sus clases; sus estructuras pueden cambiar en tiempo de ejecución.  Este nivel de flexibilidad combinado con la habilidad de la instancia de atrapar el acceso a los atributos finca las bases para varios idiomas de python.  Por otro lado también requiere de algun control del programador, porque demasiado código "dinámico" puede ser bastante difícil de entender y mantener.  Esto aplica también para los programas de Python en general; mientras que el lenguaje hace fácil realizar tareas relativamente complejas, no nos alivia a los programadores de las demandas de los principios de diseño.

3 Singleton

El patrón singleton provee un mecanismo para limitar el número de instancias de una clase a uno.  Por lo tanto el mismo objeto es siempre compartido por distintas partes del código.  Un Singleton puede ser visto como una solución más elegante para una variable global porque los datos son abstraídos por detrás de la interfaz de la clase singleton.  Primero vemos cómo puede ser implementado un singleton en C++ y luego proveemos la solución con Python con las mismas características.


class Singleton {



public:

  static Singleton& Handle();

 

private:

  static Singleton* psingle;

  Singleton();

  Singleton( const Singleton& );

  Singleton& operator=( const Singleton& );

};      


Figura 1. Definición de una clase Singleton en C++

Nuestra definición mínima de un singleton (Fig. 1) tiene solo un miembro público: el método manejador de la clase Handler.  La interfaz definida no es muy útil comotal, porque el cliente no puede hacer nada con la instancia, pero ahoranos concentramos solo en las técnicas que aseguran una solainstancia del singleton a la vez.

El constructor privado prohibe la creación de un objeto fuera de laclase.  Por ende los clientes de la clase tienen que dependeren la función miembro estática:ya sea que estacree una nueva instancia o devuelva una referencia al objeto singleton(Fig. 2 ).


Singleton& Singleton::Handle() { 
if( !psingle ) {
psingle = new Singleton;
}
return *psingle;
}

Figura 2.Método Handle de la clase

Ya que Python no tiene constructores privados debemos encontrar una alternativa para prevenir instanciaciones.  Nuestra aproximación en la figura 3 es para lanzar una excepción si el objeto singleton ya se encuentra instanciado (el atributo privado de la clase __single es distinto de None).  ¡El objeto excepción es la instancia singleton!  La desventaja de esta solución es que requiere que los clientes estén preparados para la excepción. También podría ser argumentado que utilizar excepciones para otros propósitos distintos del manejo de errores no es un muy buen estilo.  Estas consideraciones pueden ser eliminadas hasta cierto punto proveyendo un manejador similar al de la figura 2.  Esta función oculta los detalles de la instanciación poco ortodoxa y devuelve una referencia al objeto singleton (Fig. 4).


class Singleton:
    __single = None
    def __init__( self ):
       if
Singleton.__single:
           raise
Singleton.__single
       Singleton.__single
= self    

Figura 3. Clase Singleton en Python

Debido al sistema de tipos de Python el código en la figura 4 funciona para toda la jerarquía singleton.  Consideremos la siguiente situación: la clase Singleton tiene sublcases Child y Junior como se muestra en la figura 5.  Debido a que existe una relación es-un entre la clase base y la subclase solamente puede haber una instancia de toda la jerarquía. Una instancia de Singleton o Junior puede ser creada con la función Handle.  El constructor de la clase toma un parámetro -explícito- para que la instanciación deba ser hecha con una llamada directa al constructor o a una función especializada de manejo.  El siguiente código demuestra la jerarquía Singleton:

>>> child = Child( 'Monty' )
>>> junior = Handle( Junior )
>>> junior.name()
'Monty'
>>> single = Handle()
>>> single
<Child instance at d1d88>  
Algunas veces es conveniente decidir en tiempo de ejecución cuál clase singleton será instanciada.  El GOF propone un acercamiento basado en registro, donde el cliente consulta a un objeto especial registro, el cual contiene un alista de pares nombre-Singleton.  Solicitando un singleton por su nombre, elcliente recibe una referencia a la instancia correspondiente deSingleton.  Actualizar el registro puede serproblemático: ¿Quién registra unSingleton?  si es la instancia misma de Singleton (en suconstructor) !entonces todos los posibles Singleton deben serinstanciados para poder ser accesibles por los clientes del registro!

Afortunadamente la simplificación es posible a través de los conceptos de domino de nombres de Python.  Los ámbitos local y global - o en su defecto su representación de diccionario - pueden ser accedidos por funciones locales y globales.  Por ejemplo podríamosinstanciar Junior:

>>> name = 'Junior'
>>> sclass = globals()[name]
>>> junior = Handle( sclass )
>>> junior
<Junior instance at d1bd8>
Por supuesto, ahora el registro (en el ámbito del diccionario) contiene mucho más que solo información relacionada al singleton, por lo que siempre existe un riesgo de que un cliente trate alguna función como una clase Singleton.  Esta desafortunada coincidencia puede ser evitada hasta cierto puntoutilizando alguna convención de nomenclatura para lossingleton.  Un objeto de registro específico es unabuena idea aún en el ambiente Python.  Los clientesno deberían acceder a los diccionarios de ámbitodirectamente sion a través del registro.  Ahora elregistro puede detectar y denegar cualquier búsqueda desingleton que sean ilegales de acuerdo con la convención denomenclatura.  Nuevamente, esta aproximacióndepende en la cortesía de los clientes.

Tal vez la solución más segura y directa es la de registro en el nivel del módulo.  Cada módulo que contenga clases Singleton deberían también contener sentencias de inicialización que registren los nombres de las clases con el módulo de registro. Debido a que los módulos son inicializados sólo la primera vez que son importados, un cliente obtiene acceso al registro importando el módulo del registro.  En cierto modo un módulo puede ser visto como un Sigleton rudimentario.  Una ventaja de este estilo de registro explícito es la opción de implementar tantos registros sofisticados como sean necesarios.  En otras palabras podremos almacenar más información acerca de los singleton que solo los nombres de las clases, por ejemplo los nombres de los módulos, diferentes restricciones y así.  El registro puede también ofrecer diferentes servicios para las búsquedas de singleton, estadísticas y depuración, por ejemplo.



def Handle( x = Singleton ):

    try:

       single
= x()

    except Singleton, s:

       single
= s

    return
single   


Figura 4. Función Handle function



class Child( Singleton ):
    def __init__( self, name ):
       Singleton.__init__(
self )
       self.__name
= name
    def name( self ):
       return
self.__name
class Junior( Singleton ):
    pass    

Figure 5. Singleton subclasses



4 Cadena de responsabilidad

La motivación detrás de un patrón de cadena de responsabilidad es crear un sistema que pueda servir a diversas solicitudes de manera jerárquica.  En otras palabras, si un objeto que es parte de un sistema no sabe cómo responder a una solicitud en específico, la pasa a lo largo del árbol de objetos.  Como el nombre lo implica, cada objeto a lo largo de la ruta de la solicitud puede tomar la responsabilidad y atender la solicitud.

Un patrón de cadena de responsabilidad es a menudo utilizado en el contexto de interfaces gráficas de usuario donde un objeto puede contener varios otros objetos.  Como el ambiente de ventanas genera eventos los objetos o los manejan o los pasan a su objeto padre(contenedor), como en la figura 6.




Figura 6. Una jerarquía simple de ventanas


Si MsgText recibe unevento su método Handle o maneja el evento o llama almétodo Handle de SendDialog, y así sucesivamente.   MsgText no necesita saber qué objeto - si lo hay - manejará el evento; la jerarquía no tiene que ser preparada.  En otrocontexto un objeto MsgText podría comportarse similarmente y así las jerarquías pueden ser modificadas en tiempo de ejecución.  La jerarquía no requiere necesariamente ser en forma de árbol.  Una variación del patrón podría ser un sistema en el que los objetos crearan una estructura estilo anillo y algunos o todos los objetos pudieran actuar como clientes así como parte de la cadena.

La implementación del patrón en C++ es relativamente directa: en nuestro ejemplo de GUI cada objeto podría ser una subclase de la clase Widget definida en la figura 7. Mientras que esto es también una solución alcanzable en Python, no utiliza el modelo de objetos de Python a su potencial máximo.  Una aproximación más dinámica y flexible es utilizar las técnicas introducidas en el patrón de despacho de comandos de Guido van Rossum [vR97]: búsqueda y adición dinámica de atributos.




class Widget {

public:
Widget( Widget* pw ): pwParent( pw ) {}
virtual void Handle( Event );
private:
Widget* pwParent;
};

Figura 7.Definición de la clase Widget

Con Python no necesitamos restringirnos al conjunto predefinido de métodos como lo hicimos con la jerarquía Widget: el único camino para manejar este evento era llamando a Handle.  La idea detrás del patrón de despacho de comando es checar en tiempo de ejecución si el objeto tiene un método propio o no.  Nuestra clase Widget en Python(Fig. 8) provee a sus subclases con este mecanismo de despacho.  El uso de clases evento separadas (Event en la figura 8) enfatiza el hecho de que varias clases de información pueden ser transmitidas a través dela cadena.  Solamente el atributo name es asumido. Dado que nuevos atributos pueden ser añadidos dinámicamente y nuevos métodos pueden ser agregados a traves de sublcases, la interface de la clase base Event puede ser mantenida al mínimo.




class Event:
def __init__( self, name ):
self.name = name

class Widget:
def __init__( self, parent = None ):
self.__parent = parent
def Handle( self, event ):
handler = 'Handle_' + event.name
if hasattr( self, handler ):
method = getattr( self, handler )
method( event )
elif self.__parent:
self.__parent.Handle( event )
elif hasattr( self, 'HandleDefault' ):
self.HandleDefault( event )

Figura 8. Las clasesEvent y Widget en Python

Cuando un objeto evento es pasado al método Handle, una de cuatro cosas pueden pasar:

1.
Cada evento tiene su nombre asociado. Si Widget tiene un método correspondiente (llamado Handle_nombreevento), es ejecutado.
2.
Si el objeto no es el último de lacadena, pasa el objetoevento a su padre.
3.
Si el objeto tiene un manejador por defecto para toda clase de eventos(método HandleDefault) es invocado si el objeto es el último manejador posible.  Ya que HandleDefault debe siempre estar disponible sólo al final de la cadena, no es necesario ni recomendable tenerlo en la interface de la clase base.
4.
El objeto ignora el evento si no lo puede manejaro reenviar.



class MainWindow( Widget ):
def Handle_close( self, event ):
print 'MainWindow: ' + event.name
def HandleDefault( self, event ):
print 'Default: ' + event.name

class SendDialog( Widget ):
def Handle_paint( self, event ):
print 'SendDialog: ' + event.name

class MsgText( Widget ):
def Handle_down( self, event ):
print 'MsgText: ' + event.name

Figura 9. La jerarquía de clases de Widget en Python

Algunas clases Python para nuestro GUI de ejemplo son implementadas en la Figura 9.  El siguiente código construye la cadena y demuestra su uso:

>>> sd = SendDialog( mw )
>>> msg = MsgText( sd )
>>> edown = Event( 'down' )
>>> msg.Handle( edown )
MsgText: down
>>> epaint = Event( 'paint' )
>>> msg.Handle( epaint )
SendDialog: paint
>>> eodd = Event( 'odd' )
>>> msg.Handle( eodd )
Default: odd
Un aspecto curioso de nuestra solución es que un manejador de eventos no necesariamente requiere ser un método; puede también ser un objeto función.  Esto permite al programador añadir nuevos manejadores de eventos a objetos existentes en lugar de crear nuevas clases a través de la herencia.  Dadas las funciones de ámbito de Python, una función evento no puede acceder a los atributos del objeto contenedor.  Sin embargo, ya que la función evento por sí misma es un atributo puede ser borrada.  En otras palabras, las funciones evento proveen medios para crear temporalmente manejadores de eventos a una jerarquía de cadena de responsabilidad existente.  Un ejemplo simple:
... print 'Name: ' + e.name
...
>>> sd.Handle_odd = PrintName
>>> msg.Handle( eodd )
Name: odd
>>> del sd.Handle_odd
>>> msg.Handle( eodd )
Default: odd


5 Proxy

Desde la perspectiva del diseño orientado a objetos la habilidad para atrapar accesos a atributos provee algunas nuevas soluciones a viejos problemas.  Un buen ejemplo es el patrón Proxy, el cual es utilizado cuando un objeto tiene que ser escudado de sus clientes.  Puede haber varias razones para ello: conteo de referencias, distintos niveles de permisos de acceso,evaluación floja del estado de un objeto y otras. La estructura del objeto en tiempo de ejecución para el patrón es mostrada en la figura 10.  




Figura 10. Estructura del objeto en tiempo de ejecución

El cliente no necesita saber que no está accediendo al objeto real(Subject) directamente.  En otras palabras, el proxy substituye al objeto real.  En C++ esto podría significar que ambos, el proxy y la clase sujeto tienen una clase común de base.  En Python el mismo efecto puede ser logrado enmascarando la instancia del proxy como el objeto proveyendo la misma interface de métodos.  Nuestra clase proxy en la figura 11 está basada en la clase cuasiuniversal Wrapper [vR96].  Como tal, la clase Proxy es sólo una envolvente trivial para cualquier objeto. Sin embargo, su función principal es la de servir a su clase base para implementaciones más específicas.  Dado que el método __gettattr__ es llamado sólo como el último recurso el proxy puede manejar un método sujeto diferentemente "sobrecargándolo" en la claseproxy. Consideremos la siguiente situación: accedemos una instancia de la clase RGB (Fig. 12) primero directamente, luego utilizando una instancia de Proxy genérica como una envolvente y finalmente a través de una instancia de NoBlueProxy (Fig. 13):  

>>> rgb = RGB( 100, 192, 240 )
>>> rgb.Red()
100
>>> proxy = Proxy( rgb )
>>> proxy.Green()
192
>>> noblue = NoBlueProxy( rgb )
>>> noblue.Green()
192
>>> noblue.Blue()
0



class Proxy:
def __init__( self, subject ):
self.__subject = subject
def __getattr__( self, name ):
return getattr( self.__subject, name )

Figura 11. Clase base Proxy




class RGB:
def __init__( self, red, green, blue ):
self.__red = red
self.__green = green
self.__blue = blue
def Red( self ):
return self.__red
def Green( self ):
return self.__green
def Blue( self ):
return self.__blue

Figura 12. La clase Subject




class NoBlueProxy( Proxy ):
def Blue( self ):
return 0

Figura 13. Una implementación más específica de proxy

Mientras que python provee algunos mecanismos que son usualmente implementados con el patrón Proxy (por ejemplo el conteo de referencias simples)el patrón es todavía altamente aplicable a la comunidad Python.  Un excelente ejemplo es el paquete deDaniel Larsson RemoteCall [Lar97], que provee un mecanismo simple para aplicaciones distribuidas.  Uno de los conceptos clave en el sistema es el rol de agente, el cual es una representación del lado del cliente de los objetos del lado del servidor - en otras palabras, proxies.  La historia del paquete también demuestra un aspecto digno de mencionar de los patrones en general: el autor reconoció que había aplicado un patrón después después de su implementación.

6 Conclusiones

Los patrones del GOF y los de Python pueden beneficiarse mutuamente.  Mientras quea Python le faltan algunas características que el GOF asume, no es imposible construir implementaciones de los patrones que funcionen como sus contrapartes del GOF. Nuestra implementación del Singleton sustituyó los constructores privados con un mecanismo de excepción y un método de clase con una función regular pero el patrón subyacente es claramente el Singleton.  Es importante notar la diferencia entre un patrón y suimplementación.  En Singleton nuestro enfoque principal era mantener las características del patrón descritas en el GOF. Esto guía a una solución que probablemente es muy obscura para ser reconocida como un patrón Python.  Una solución más intuitiva - y más del estilo Python tal vez - sería abandonar los problemas de encapsulación y proveer simplemente una función fábrica para la instancia Singleton [Lun97].

El uso de Python como un lenguaje de implementaciones puede también reforzarel patrón.  La naturaleza flexible y dinámica del lenguaje provee una buena base para una variedad de distintas y elegantes soluciones.  Nuestra cadena de responsabilidad hace el patrón más dinámico al implementarlo con el patrón despacho de comandos, el cual toma ventaja de las capacidades de Python en tiempo de ejecución.  Estas capacidades incluyen búsquedas y adiciones de atributos en tiempo de ejecución.  En otras palabras, las instancias de una misma clase no necesariamente requieren tener estructura idéntica y el acceso a los atributos puede ser monitoreado y atrapado.  Estos mecanismos proveen una forma bastante elegante de implementar varias clases genéricas como en nuestra implementación del Proxy.

Elegimos tres patrones para enfatizar los problemas discutidos, pero tenemos la confianza de que otros patrones del GOF pueden ser implementados en Python, y con resultados similares.  Mientras que los patrones pudieran terminar con características flexibles y dinámicas después del proceso, no necesariamente erosionaría la usabilidad y reconocibilidad de los patrones.  La familiaridad y el orden provisto por el uso de patrones puede ofrecer mayores beneficios para diversos sistemas de objetos y frameworks Python.


Referencias

BMR+96
Frank Buschmann, Regine Meunier, Hans Rohnert, Peter
Sommerlad, and Michael Stal.
Pattern-Oriented Software Architecture -- A System of
Patterns
.
John Wiley & Sons, 1996.
457 pages.
Dub96
Paul Dubois.
Introduction to Python .
Tutorial at TOOLS USA 96, August 1996.
GHJV95
Erich Gamma, Richard Helm, Ralph Johnson, and John
Vlissides.
Design Patterns -- Elements of Reusable Object-Oriented
Software
.
Addison-Wesley, 1995.
395 pages.

Lar97
Daniel Larsson.
RemoteCall package.
http://www.python.org,
September 1997.
Lun97
Fredrik Lundh.
Singleton Pattern .
Python Pattern-SIG mailing list, July 1997.
http://www.python.org.

vR96
Guido van Rossum.
Python Tutorial, October 1996.
http://www.python.org.

vR97
Guido van Rossum.
Command Dispatch Pattern .
Python Pattern-SIG mailing list, May 1997.
http://www.python.org.


Creado por argon
Última modificación 2005-08-24 19:39

Última actualización: 2007-04-29 10:56:59-05

Printable version

Add comment:



Captcha




Que estas haciendo?
teoshoteosho está:
Preparandome para el viaje a Puerto Vallarta... que triste...
1 hour, 22 minutes ago

scarecrowscarecrow está:
Du hast?
5 hours, 25 minutes ago

der_teufelder_teufel está:
Ich habe einen Kater, aber nicht so schlecht...
13 hours ago

rnstuxrnstux está:
Y yo un Abrazo.
1 day, 12 hours ago

saidjosesaidjose está:
Dandole su habrazote a mi santa madre que me soporta
1 day, 14 hours ago

dsquiddsquid está:
esperando a que este el pozole
1 day, 16 hours ago

Que estuvimos haciendo >>
Chipotle Software

En tu equipo tienes:
Sólo Windows
Windows y Linux
Sólo Linux
Linux y un BSD
Solaris, linux y BSD
Sólo MacacOS
Rapiditas
Problemas de Lenguaje en niños
10410 lecturas
Sexualidad infantil y juvenil
9166 lecturas
Anticoncepción de Emergencia
7840 lecturas
Rompiendo cualquier clave WEP en unos pocos minutos
6917 lecturas
Sinapsis y exocitosis
6227 lecturas
Mi primer CakePHP, mmmmm cakeee
5260 lecturas
Evolución filética en las hepáticas
4697 lecturas
BASH y Primeros Comandos
4012 lecturas
CakePHP II Active Record
3742 lecturas
Cómo convertirse en hacker
3619 lecturas
Add to Technorati Favorites

ir arriba
Casarse está bien. No casarse está mejor. San Agustín.

The Queen is here Mozilla Firefox The Best DataBase CakePHP Framework XHTML GNU Hacker Chipotle Software

Too Cool for Internet Explorer