viernes, septiembre 02, 2005

La magia del Smalltalk: Capítulo 11 - Manteniendo la identidad



Ya hemos hablado, en posts anteriores, de la identidad de los objetos en Smalltalk y como eso impacta en la forma de trabajo.

En los lenguajes de programación "tradicionales" los objetos no pueden sobrevivir a una ejecución del programa. Por eso se usan técnicas donde se reemplaza la identidad con IDs, claves únicas, etc.

También es muy frecuente encontrar programas donde más de una instancia en el modelo por cada objeto real con los consiguientes típicos problemas de duplicación. Ni hablar de los casos típicos de tener objetos que no pueden sobrevivir una conexión/sesión/etc. He visto muchos sistemas donde lo único importante, para el funcionamiento interno, es el ID de los objetos.



A grandes problemas, soluciones simples: Cuando modelamos la realidad (es decir, cuando estamos programando) conviene vincular el ciclo de vida de nuestros objetos al ciclo de vida de los objetos de la vida real.

Por ejemplo: Si estamos modelando uno de esos clásicos sistemas de facturación, conviene que los objetos ArticuloDeVenta de nuestro modelo vivan tanto como viven los Artículos de Venta reales. Si tenemos un artículo para la venta disponible por 10 años, conviene que la instancia que representa a dicho artículo viva los mismos 10 años. Si tenemos sólo 1 artículo real para la venta, no instanciamos más 1 instancia en nuestro modelo.

En un mundo ideal, instanciamos en nuestro ambiente el artículo en cuanto la empresa decide venderlo y este objeto desaparecerá cuando nadie lo use ni lo recuerde.

Lamentablemente el mundo dista bastante de ser ideal y muchas veces tenemos que convivir con feas bases de datos relacionales, archivos de texto y demás cosas que no nos permiten utilizar la identidad de Smalltalk ya que los objetos "viven" fuera.

Supongamos que tenemos que hacer un lindo sistemita en Smalltalk que tenga que ir a buscar los datos a una fea base de datos relacional. Esa base de datos se actualiza desde fuera de nuestro programa, así que tenemos que descartar la alternativa de importar todos los datos a nuestra imagen.

Una solución muy elegante es mantener la identidad dentro de nuestro ambiente siempre que sea necesario. Para eso tendremos en nuestro (casi seguro Singleton) objeto Sistema (el sistema es un objeto, ¿no?) un método del tipo #findXXXByID:.

Veamos un poco de código usando la recién estrenada opción “copiar como html" que acabo de subir al update-stream del Squeak de Small-Land versión 3.8.

SampleSystem>>initialize
    "initialize the receiver"
    customers := WeakValueDictionary new


SampleSystem>>findCustomerByID: aString
    "Answer the customer represented by the given ID"
    | customer |

    customer := customers
                            at: aString
                            ifAbsent: [nil].

    ^ customer
                ifNil: [self loadCustomerID: aString].


SampleSystem>>loadCustomerID: aString
    "PRIVATE - load the data from the outer space"
    << CARGAR LOS DATOS DE LA BASE DE DATOS Y CREAR UNA INSTANCIA >>


Gran parte del truco de este idiom/patrón/nombreQueLesGuste reside en usar el diccionario WeakValueDictionary. Ese diccionario mantiene una referencia débil (Weak), una referencia débil es una forma de referenciar a un objeto que NO evita que el garbage collector lo elimine.

La forma de utilizar referencias débiles es muy sencilla, se guardan los objetos en este tipo de colecciones (WeakValueDictionary, WeakKeyDictionary, WeakSet, WeakArray, etc.) y estos vivirán mientras alguien mantenga una referencia fuerte (una referencia normalita, las de toda la vida). Si el objeto está sólo referenciado en forma débil, en algún momento el garbage collector se lo llevará y nos dejará un nil en su lugar. Así que la única precaución que debemos tomar es considerar que la colección nos devuelva un nil.

Veamos el código de arriba... mientras alguien mantenga una referencia fuerte (las normales, las de toda la vida) el WeakValueDictionary va a mantener una referencia débil y nuestro método #findCustomerByID: devolverá siempre esa misma instancia manteniendo la identidad. Para el mismo ID siempre devolvemos la misma instancia. En cuanto todos se olviden del objeto en cuestión, y el garbage collector decida llevarse la instancia, nuestra colección olvidará al customer en cuestión.

1 Comentarios:

At 2/9/05 13:54, Anonymous Anónimo said...

Sabes que no me apasiona Smalltalk, pero caigo rendido cada vez que explicas algo de objetos...
Cuanto más te leo hablar sobre Patrones y objetos en Smalltalk más se agranda mi pasión por PHP 5.

 

Publicar un comentario

<< Principal