jueves, 3 de diciembre de 2009

Videos de Smalltalks 2009

Que tal,
James Foster está publicando los videos que filmó es su blog. La verdad que se está tomando un trabajo impresionante porque los está combinando con las slides, muchas gracias James!!
Acá está el link a la apertura del congreso http://www.youtube.com/watch?v=UzRZmFKxqO4
Y aca el link al blog de James http://programminggems.wordpress.com

jueves, 5 de noviembre de 2009

Jornada de Calidad

Un post para mi olvidado blog :-)
Voy a dar una charla de TDD en las Jornadas de Calidad de la Ort: http://campus.ort.edu.ar/ceo/noticia/61244/jornada-de-calidad-de-software

Por otro lado no olviden de verificar www.fast.org.ar dentro de poco estará el cronograma publicado!

domingo, 23 de agosto de 2009

Smalltalks 2009 - Tercera Conferencia Argentina de Smalltalk

Del Jueves 19 al Sábado 21 de Noviembre del corriente año,
se llevará a cabo en la Facultad de Ciencias Exactas y Naturales (FCEN) de
la Universidad de Buenos Aires, la Tercera Conferencia de Smalltalk de Argentina,
denominada "Smalltalks 2009"

Todas aquellas personas que estén relacionadas con Smalltalk, sean
docentes, alumnos, investigadores, desarrolladores o empresarios, están
invitados a participar como oyentes o expositores de manera gratuita.
El sitio para inscribirse será anunciado en breve.

El objetivo de la conferencia es reunir a la comunidad Smalltalk Argentina e Internacional
para estrechar vínculos compartiendo trabajos,
experiencias y vivencias relacionadas con esta tecnología o temas afines.

Esta edición de la conferencia categorizará las presentaciones en "Industria y Desarrollo" e "Investigación y Educación", contará con un concurso de programación y nos visitará, nuevamente, gente reconocida de la comunidad Smalltalk internacional.

En la "Categoría de Investigación/Educación" se expondrán aquellos trabajos de
investigación y educación que se estén realizando o se hayan realizado con
Smalltalk, en ámbitos de universidades y establecimientos públicos o
privados.

En la "Categoría de Industria y Desarrollo" se expondrán trabajos relacionados a la
Creación de Software utilizando Smalltalk, por medio de empresas o personas, con fines comerciales o no.

Algunos temas sugeridos para las presentaciones, de manera no excluyente son:
* Herramientas de Desarrollo
* Desarrollo con Prototipos
* Model Driven Development
* Prácticas Ágiles (XP, TDD, etc)
* Desarrollos de Aplicaciones Web
* Meta-Modeling / Meta-Programación
* Nuevos Modelos o Frameworks implementados
* Material Educativo
* Sistemas Embebidos y Robótica
* SOA y Web services
* Reportes de Experiencias de Desarrollo o Investigación
* Sistemas comerciales
* Testing: Prácticas y automatización
* Interoperabilidad con otras tecnologías
* Best Practices de Diseño y Arquitectura

Aquellos interesados en exponer, deben tener en cuenta los siguientes
hitos y tareas:

1) Deben presentar un Resumen de la presentación a más tardar para el
Lunes 19 de Octubre de 2009. Dicho Resumen no debe superar una carilla
y debe incluir mínimamente una descripción del trabajo a presentar sin
omitir el objetivo del mismo y la categoría a la cuál pertenece la presentación.
El mecanismo de presentación del Resumen será anunciado en breve.

2) El Comité de Programa de la Conferencia definirá para el Lunes 26 de
Octubre aquellos trabajos que serán elegidos para ser presentados durante
la jornada a partir de los Resúmenes recibidos según lo especificado
en el punto anterior. Se dará mayor importancia a aquellos trabajos con
capacidad de mostrar ejemplos dinámicos y no únicamente presentaciones
estáticas.

3) Los trabajos y presentaciones realizadas serán publicadas en el sitio de la conferencia una vez finalizada la misma.

4) La definición del Cronograma de la Conferencia se publicará a través
de la página web el Miércoles 28 de Octubre.

Respecto del concurso de programación, todos aquellos que deseen participar pueden interiorisarse del mismo en el grupo smalltalks-2009-coding-contest@googlegroups.com
La intención del concurso es pasar un buen momento, disfrutar solucionando un problema en Smalltalk y ganar un premio como reconocimiento del trabajo realizado.
Estamos trabajando fuertemente para que todos puedan participar del mismo e iremos presentando ejemplos y prototipos que puedan ser tomados de ejemplo para desarrollar mejores soluciones.

Para información sobre cómo llegar a la FCEyN, pueden acceder al sitio del departamento de computación: http://dc.uba.ar
Por favor, difundan esta invitación en aquellas listas y grupos que consideren interesados.

Los esperamos a todos!
Comité Organizador de Smalltalks 2009

jueves, 9 de julio de 2009

Bing vs. Google


Fijensé el resultado de hacer http://bing-vs-google.com/?q=bing (o sea, buscar bing y comparar los resultados de google y bing).
No siempre el resultado será siempre el mismo, les dejo una imagen que muestra la diferencia...

Una conclusión interesante sobre la ley de Moore y un dicho de Alan Kay

No tiene desperdicio: http://lists.canonical.org/pipermail/kragen-tol/2007-March/000850.html

¿Quizá debería ver VisualAge/Envy un poco?

Sino, no haría el comentario del final :-) http://lambda-the-ultimate.org/node/3459

Algo que venimos enseñando hace más de 10 años

Los diagramas de diseño tienen como objetivo comunicar. Me alegro que Rebecca lo ponga bien claro es su blog: http://www.wirfs-brock.com/2009/06/value-of-design-documentation.html

viernes, 3 de julio de 2009

Respuesta: ¿Cómo mejorar esta clase?

El otro día postee una pregunta sobre cómo mejorar la clase MouseOverHandler de Squeak/Pharo... no hubo ningún comentario lo que creo que significa que nadie se animó a hacerlo! (o claro, nadie leyó el post...). En fin, creo que nadie se animó a hacerlo porque realmente no es fácil y sólo ver el código desalienta a cualquiera, pero bueno, yo lo hice hace una para de semanas atrás así que les comento cómo.
Primero, ¿cuál es el problema con esta implementación? El principal es que el método #processMouseOver: es muy difícil de entender. Es muy largo y complicado, así que hay que empezar por tratar de entender que hace y mejorarlo. Para ello vamos a utilizar el refactoring de extract method para reemplazar colaboraciones por mensajes que revelen su intención. Lo interesante es que el método está comentado justamente en las secciones que vamos a extraer, lo cual muestra como a veces en vez de comentar código simplemente hay que escribirlo con intention revealing. Veamos como queda.
Reemplacemos este código:

hand := anEvent hand.
leftMorphs := mouseOverMorphs asIdentitySet.
"Assume some coherence for the number of objects in over list"
overMorphs := WriteStream on: (Array new: leftMorphs size).
enteredMorphs := WriteStream on: #().

Por:
self initializeProcessMouseOver.

Luego reemplacemos:
"Now go looking for eventual mouse overs"
hand handleEvent: anEvent asMouseOver.
"Get out early if there's no change"
(leftMorphs isEmpty and: [enteredMorphs position = 0])
ifTrue: [^leftMorphs := enteredMorphs := overMorphs := nil].
focus := hand mouseFocus.

Por:
self handleAsMouseOver: anEvent.

También reemplacemos: (no se preocupen en leer todo el código, simplemente tomen un dimensión de qué estamos reemplazando y por quién)
"Send #mouseLeave as appropriate"
evt := anEvent asMouseLeave.
"Keep the order of the left morphs by recreating it from the mouseOverMorphs"
leftMorphs size > 1
ifTrue: [leftMorphs := mouseOverMorphs select: [:m | leftMorphs includes: m]].
leftMorphs do:
[:m |
(m == focus or: [m hasOwner: focus])
ifTrue:
[localEvt := evt transformedBy: (m transformedFrom: hand).
m handleEvent: localEvt]
ifFalse: [overMorphs nextPut: m]].

Por:
self handleAsMouseLeave: anEvent.

También reemplacemos:
"Send #mouseEnter as appropriate"
evt := anEvent asMouseEnter.
enteredMorphs ifNil:
["inform: was called in handleEvent:"

^leftMorphs := enteredMorphs := overMorphs := nil].
enteredMorphs := enteredMorphs contents.
enteredMorphs reverseDo:
[:m |
(m == focus or: [m hasOwner: focus])
ifTrue:
[localEvt := evt transformedBy: (m transformedFrom: hand).
m handleEvent: localEvt]].

Por:
self handleAsMouseEnter: anEvent.

Y por último:
"And remember the over list"
overMorphs ifNil:
["inform: was called in handleEvent:"

^leftMorphs := enteredMorphs := overMorphs := nil].
mouseOverMorphs := overMorphs contents.
leftMorphs := enteredMorphs := overMorphs := nil

Por:
self rememberOverList

Bueno, no se que les parece (o si quedó claro como lo escribí), pero simplemente hay que agarrar cada conjunto de colaboraciones que tiene un comentario sobre que hacen y extraerlas a otro método. En conclusión con algunos cambios más el método #processMouseOver: quedá así:

processMouseOver: anEvent

self initializeProcessMouseOver.
self handleAsMouseOver: anEvent.
self hasLeftMorphsChanged ifTrue: [
self handleAsMouseLeave: anEvent.
self handleAsMouseEnter: anEvent.
self rememberOverList ].

self initializeTrackedMorphs

¿Se entiende mejor qué hace no?. Fijensé como un cambio tan sencillo, simplemente usando extract method, mejora el diseño considerablemente. Por supuesto que hay que seguir utilizando esta técnica para cada uno de los nuevos métodos porque siguen siendo muy complejos y al hacerlo se puede ver que hay código repetido, de hecho el mismo refactoring browser al realizar la extracción lo sugiere. No voy a mostrar todo este proceso porque es similar al anterior y no quiero hacer el post muy pesado (más de lo que es!).
Por último, también realicé otros cambios como por ejemplo no usar nil para inicializar las variables de instancia que utiliza para realizar el procesamiento, sino inicializarlas con colecciones vacías de tal manera que nunca se produzca un MNU.
En conclusión, el código quedó así:

Object subclass: #MouseOverHandler
instanceVariableNames: 'mouseOverMorphs enteredMorphs overMorphs leftMorphs'
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic-Events'!
-------------------------------------------------------------------------------
processMouseOver: anEvent

self initializeProcessMouseOver.
self handleAsMouseOver: anEvent.
self hasLeftMorphsChanged ifTrue: [
self handleAsMouseLeave: anEvent.
self handleAsMouseEnter: anEvent.
self rememberOverList ].

self initializeTrackedMorphs
-------------------------------------------------------------------------------
initializeProcessMouseOver

leftMorphs := mouseOverMorphs asIdentitySet.
overMorphs := WriteStream on: (Array new: leftMorphs size).
enteredMorphs := WriteStream on: #()! !
-------------------------------------------------------------------------------
handleAsMouseEnter: anEvent

| asMouseEnterEvent |

asMouseEnterEvent := anEvent asMouseEnter.
enteredMorphs := enteredMorphs contents.
enteredMorphs reverseDo: [ :anEnteredMorph |
self inform: asMouseEnterEvent to: anEnteredMorph originatedFrom: anEvent ifNotFocusedDo: [] ]
-------------------------------------------------------------------------------
handleAsMouseLeave: anEvent

self keepLeftMorphsOrder.
self informMouseLeaveToLeftMorphsUsing: anEvent
-------------------------------------------------------------------------------
handleAsMouseOver: anEvent

anEvent hand handleEvent: anEvent asMouseOver.
-------------------------------------------------------------------------------
noticeMouseOver: aMorph event: anEvent
"Remember that the mouse is currently over some morph"

leftMorphs remove: aMorph ifAbsent: [ enteredMorphs nextPut: aMorph ].
overMorphs nextPut: aMorph.
-------------------------------------------------------------------------------
initialize
mouseOverMorphs := #().

self initializeTrackedMorphs
-------------------------------------------------------------------------------
initializeTrackedMorphs

leftMorphs := OrderedCollection new.
overMorphs := WriteStream on: #().
enteredMorphs := WriteStream on: #().
-------------------------------------------------------------------------------
hasLeftMorphsChanged

^(leftMorphs isEmpty and: [ enteredMorphs position = 0 ]) not
-------------------------------------------------------------------------------
inform: evt to: aLeftMorph originatedFrom: anEvent ifNotFocusedDo: aBlock

^ (self is: anEvent withFocusOver: aLeftMorph)
ifTrue: [ self transform: evt from: anEvent andSendTo: aLeftMorph ]
ifFalse: aBlock
-------------------------------------------------------------------------------
informMouseLeaveToLeftMorphsUsing: anEvent

| asMouseLeaveEvent |

asMouseLeaveEvent := anEvent asMouseLeave.

leftMorphs do: [ :aLeftMorph |
self inform: asMouseLeaveEvent to: aLeftMorph originatedFrom: anEvent ifNotFocusedDo: [ overMorphs nextPut: aLeftMorph ] ]
-------------------------------------------------------------------------------
is: anEvent withFocusOver: aMorph

| focusedMorph |

focusedMorph := anEvent hand mouseFocus.
^ aMorph = focusedMorph or: [ aMorph hasOwner: focusedMorph ]
-------------------------------------------------------------------------------
keepLeftMorphsOrder

leftMorphs size > 1 ifTrue: [ leftMorphs := mouseOverMorphs intersection: leftMorphs ]
-------------------------------------------------------------------------------
rememberOverList

mouseOverMorphs := overMorphs contents.
-------------------------------------------------------------------------------
transform: anEvent from: originalEvent andSendTo: aMorph

| transformedEvent |

transformedEvent := anEvent transformedBy: (aMorph transformedFrom: originalEvent hand).
^ aMorph handleEvent: transformedEvent
-------------------------------------------------------------------------------
¿Mucho más claro no?

jueves, 2 de julio de 2009

miércoles, 1 de julio de 2009

Quiz: ¿Cómo mejorar esta clase?

Que tal,
les dejo un problema para pensar, no es muy largo ni difícil, con 10 minutos imagino que pueden encontrar la manera de resolverlo. El problema es el siguiente: hay que mejorar el código de la clase MouseOverHandler que se encuentra en Squeak o Pharo, en particular el método #processMouseOver: anEvent.
El motivo por el cual seleccioné este problema se debe a que últimamente en la imagen de Pharo en condiciones aún no encontradas, el método #noticeMouseOver: aMorph event: anEvent envía el mensaje #includes: a nil.
Lo interesante del problema es:
1) Entender como funcionan las instancias de esta clase
2) Encontrar el motivo de por qué funciona incorrectamente el mensaje #noticeMouseOver: aMorph event: anEvent
3) Pensar cómo se puede generar información que confirme lo encontrado en el punto 2
4) El objetivo de este post, mejorar el diseño de esta clase.
Acá está el código por si no tienen ganas de abrir un Squeak/Pharo.
---------------------------
Object subclass: #MouseOverHandler
instanceVariableNames: 'mouseOverMorphs enteredMorphs overMorphs leftMorphs'
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic-Events'
---------------------------
initialize
super initialize.
mouseOverMorphs := #().
---------------------------
processMouseOver: anEvent
"Re-establish the z-order for all morphs wrt the given event"

| hand localEvt focus evt |
hand := anEvent hand.
leftMorphs := mouseOverMorphs asIdentitySet.
"Assume some coherence for the number of objects in over list"
overMorphs := WriteStream on: (Array new: leftMorphs size).
enteredMorphs := WriteStream on: #().
"Now go looking for eventual mouse overs"
hand handleEvent: anEvent asMouseOver.
"Get out early if there's no change"
(leftMorphs isEmpty and: [enteredMorphs position = 0])
ifTrue: [^leftMorphs := enteredMorphs := overMorphs := nil].
focus := hand mouseFocus.
"Send #mouseLeave as appropriate"
evt := anEvent asMouseLeave.
"Keep the order of the left morphs by recreating it from the mouseOverMorphs"
leftMorphs size > 1
ifTrue: [leftMorphs := mouseOverMorphs select: [:m | leftMorphs includes: m]].
leftMorphs do:
[:m |
(m == focus or: [m hasOwner: focus])
ifTrue:
[localEvt := evt transformedBy: (m transformedFrom: hand).
m handleEvent: localEvt]
ifFalse: [overMorphs nextPut: m]].
"Send #mouseEnter as appropriate"
evt := anEvent asMouseEnter.
enteredMorphs ifNil:
["inform: was called in handleEvent:"

^leftMorphs := enteredMorphs := overMorphs := nil].
enteredMorphs := enteredMorphs contents.
enteredMorphs reverseDo:
[:m |
(m == focus or: [m hasOwner: focus])
ifTrue:
[localEvt := evt transformedBy: (m transformedFrom: hand).
m handleEvent: localEvt]].
"And remember the over list"
overMorphs ifNil:
["inform: was called in handleEvent:"

^leftMorphs := enteredMorphs := overMorphs := nil].
mouseOverMorphs := overMorphs contents.
leftMorphs := enteredMorphs := overMorphs := nil
---------------------------
noticeMouseOver: aMorph event: anEvent
"Remember that the mouse is currently over some morph"
(leftMorphs includes: aMorph)
ifTrue:[leftMorphs remove: aMorph]
ifFalse:[enteredMorphs nextPut: aMorph].
overMorphs nextPut: aMorph.

Alguien está necesitado de dinero...

Bueno, quién no? pero miren esto:
http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&item=180375524276
Inicialmente me hace pensar en dos cosas:
1) Ya nadie se gana la vida escribiendo libros (o por lo menos libros técnicos)
2) Kent Beck no es muy bueno haciendo negocios :-)
El resto de las posibilidad lo dejo par el ideario colectivo :-)

martes, 23 de junio de 2009

El sitio está listo!

Quería comentarles que ya tenemos listo el sitio de Internet, la dirección es http://www.10pines.com/
Aún está en inglés, pero pronto lo pasaremos al castellano.
Espero que les guste! si encuentran algún error o tiene sugerencias, será bienvenido.

domingo, 31 de mayo de 2009

Charla en ESUG

Algunos me preguntaron de que voy a hablar en ESUG. Mi intención es hablar sobre un trabajo de tesis que están haciendo Nicolas Chillo y Gabriel Burnstein que trata de Mutation Testing.... veremos si es aceptado...
Gracias por los comentarios sobre el viaje!

viernes, 22 de mayo de 2009

Voy a ESUG!!!

Si, nuevamente voy a ESUG!!! (www.esug.org)
ESUG es la conferencia de Smalltalk de Europa. Es una conferencia muy linda que tuve la oportunidad de asistir en 2005. Lo interesante es que este año vamos muchos argentinos y la mayoría daremos una charla...

martes, 14 de abril de 2009

Magritte 2

Algo que siempre digo y me olvidé de comentar en el post anterior es que "es muy fácil criticar pero difícil hacer". Mis comentarios del post anterior fueron todas crítticas, sin embargo Magritte también tiene cosas muy interesantes como el manejo de mementos para la edición de objetos, la posibilidad de configurar el framework (todo puede ser cambiado, como el container, las descripciones, los componentes), etc. 
Una aclaración que me parece necesaria para no parecer siempre negativo.

Magritte

No voy a escribir sobre arte, no, este post no es sobre René Magritte sino sobre el framework creado por Lukas Renggli para ayudar en el desarrollo de aplicaciones utilizando metraprogramación.
Ayer estuve leyendo bastante sobre el mismo y jugando un poco con él. Es un framework que permite rápidamente obtener una representación visual de objetos del modelo, creando en ellos una serie de descripciones. Por ejemplo, si tenemos la clase Address, creando métodos que se llaman "descriptionXxx" donde Xxx generalmente tienen que ver con los nombres de las variables de instancia, se puede obtener un componente SeaSide o Morph para instancias de Address.
Veamos un ejemplo del tutorial que ofrecen:

descriptionStreet
^ MAStringDescription new
autoAccessor: 'street';
label: 'Street';
priority: 10;
yourself

Esta sería la descripción para la variable de instancia 'street' de Address (o MAAdressModel como se llama en el tutorial... un nombre poco feliz desde mi punto de vista puesto que la palabra Model está de más). Veamos que significa cada cosa:
1) autoAccessor: Significa que se accederá a 'street' con los mensajes #street y #street:, si no existen los creará, pero no solo eso, creará también la var. de instancia si no existe.
2) label: String que se utilizará para identificar el entry field de Street
3) priority: Posición en la que debe aparecer el campo en el componente visual. Menos prioridad significa más arriba, más prioridad significa más abajo.

Veamos ahora otra "descripción":
descriptionPlz
^ MANumberDescription new
autoAccessor: 'plz';
label: 'PLZ';
priority: 20;
min: 1000;
max: 9999;
yourself

Este describe el código postal. Más observaciones:
4) Nótese que priority es 20, lo que significa que irá luego de street... no se si notaron que hay 10 números entre el priority de street y este, ¿por qué?, para poder poner componentes visuales entre street y código postal sin necesidad de modificar estos métodos.
5) min: y max: indican los valores numéricos máximos del código postal

Por último, quería mostrar esta "descripción" también:
descriptionCanton
^ MASingleOptionDescription new
options: #( 'Zurich' 'Bern' 'Luzern' 'Uri' 'Schwyz' 'Unterwalden' 'Glarus' 'Zug' 'Freiburg' 'Solothurn' 'Basel' 'Schaffhausen' 'Appenzell' 'St. Gallen' 'Graubunden' 'Aargau' 'Thurgau' 'Ticino' 'Vaud' 'Valais' 'Neuchatel' 'Geneve' 'Jura' );
reference: MAStringDescription new;
autoAccessor: 'canton';
label: 'Canton';
priority: 40;
beSorted;
yourself

Es la utilizada para elegir un canton (provincia). Observaciones:
6) Los distintos cantones están hardcodeados en la descripción

Bien, ahora voy a dar algunas opiniones sobre los puntos que estuvimos viendo:
1) Nunca usaría autoAccessor. No sirve cuando se está desarrollando un app en serio puesto que crearía una variable aún cuando el nombre de la misma está mal escrito, es muy peligroso dejar la creación de variables y métodos a herramientas en vez de hacerlo uno. Por otro lado, si se hace un rename de la variable este "descriptor" no se enteraría... ese es uno de los problemas de este tipo de frameworks, la falta de integración con las herramientas de desarrollo comúnes y también por no estar reificadas las variables en Smalltalk. Para solucionarlo utiliaría #selectorAccessor: o escribiría un test que se asegurará la consistencia de este descriptor.
2) Sobre label. Acá es donde me empieza a gustar menos la cosa. En una clase del modelo (Address) estoy poniendo información sobre que quiero que aparezca en la view, un acoplamiento poco deseado. Además este label podría cambiar de view en view. La solución que proponen es obtener una copia del descriptor y modificar el label para ese caso... hmm, más acoplamiento.
3) y 4) priority: Más acoplamiento. Entre el modelo y la view por estar este número en un método del modelo y entre los descriptores mismos por utilizar entre ellos un número mayor que el otro para indicar que va después. Para el primer caso, ¿qué le importa al modelo en que posición se va a mostrar el componente visual para alguna de sus variables de instancias?. Para el segundo caso, ¿no sería mejor tener un objeto que indique la secuencia en la que debe ir cada componente visual? una OrderedCollection de descriptores por ejemplo?. De esa manera el hecho de la posición no estaría acoplado al descriptor sino que sería responsabilidad de otro objeto. Por otro lado, ¿por qué dejar 10 número entre uno y otro? ¿no se pueden usar número reales? Si se puediesen poner números no enteros (cosa que no probé aún) no habría necesidad de dejar 10 números entre una descripción y otra puesto que todos sabemos que siempre hay números entre dos números reales. 
5) Que las validaciones de los objetos no estén con los mismos no me parece una buena solución. Si el código postal de Suiza tiene que estar entre 1000 y 9999, debería existir una abstracción para dicho ente que además debería encargarse de realizar esta validación. Separar la validación del objeto en si mismo no representa correctamente el ente que se está modelando, lo cual trae problemas a posteriori, como por ejemplo cargar direcciones desde un archivo. En ese caso, ¿quién valida que el código postal sea un número entre 1000 y 9999?, ¿o para hacerlo también tengo que usar Magritte?. Digamos que uso Magritte, ¿qué pasa entonces cuando creo una dirección desde el workspace? no voy a usar Magritte para eso... 
6) Por más que es un ejemplo y los cantones están hardcodeados por ello (imagino), supongamos que no, ¿cómo los obtendría?. Tendría que colaborar con el objeto encargado de mantener la lista de cantones válidos. Algunos propondría utilizar una clase, "Cantones" por ejemplo que tenga una class variable con las cantones válidos, una porquería desde mi punto de vista. Si vamos a hacer eso utilicemos una variable global, "Cantones" y listo, ¿para qué crear una clase?. Pero sea una u otra la solución, el acoplamiento que se crea es enorme! ahora esa descripción no va a funcionar con otra cosa que no sea esa lista de cantones! que además está referenciado desde un método de la clase Address! o sea que no puedo usar Address en otro contexto!... una muy mala solución y que surge de la necesidad de indicarle los cantones en la "descripción" en vez de indicarselos a la view directamente al momento de construirla.

En fin, mi conclusión por ahora es que Magritte, como todo este tipo de frameworks, sirve para hacer una app rápido y chiquita si no te importa mucho el diseño y cómo la vas a mantener. Ahora, si querés hacer una app en serio o no utilices Magritte o hay que tomar algunas decisiones como:
1) Que las descripciones no estén en el modelo
2) Que el orden de los componentes estén en otro objeto y sea configurable por view
3) Que la validaciones no estén en las descripciones
4) Que no exista acoplamiento entre la descripción y los elememtos que muestran las views

Una última cosa que acabo de acordarme, me parece una decisión muy poco feliz la que tomaron de que todos los objetos sepan responder #description para devolver una colección de descriptores de Magritte. ¿por qué no lo llamaron #magritteDescription o algo así y de esa manera no impedían utilizar ese mensaje en el modelo para cosas que tengan que ver con el modelo?.... en fin.
Voy a mandar un mail a la lista de Magritte con estos comentario para ver que soluciones/respuesta obtengo. Los mantendré informados.


Mapeo Objeto-Relacional

El mapeo de objetos a base de datos relacionales es un tema bastante complicado y estudiado en nuestra profesión, aunque siempre aparecen nuevos frameworks que supuestamente solucionan los problemas que se generan en este mapeo, pero que finalmente crean nuevos... en fin, vamos a tener que esperar mucho hasta que la gente se de cuenta que el problema es de fondo y no de forma... mientras tanto les paso un link que me recomendaron sobre este tema:

viernes, 3 de abril de 2009

Nueva Etapa Laboral

Aquellos que tengan Facebook habrán visto que a principio de semana puse que estoy empezando una nueva etapa laboral... no comenté muchos detalles allí, así que quería utilizar este medio para aclarar un poco más de que se trata.
Con unos amigos estamos armando una empresa en la que ofrecemos servicios de desarrollo de aplicaciones complejas, capacitación y coaching de objetos, patrones, frameworks, tdd, metodologías ágiles y diagnósticos sobre código y diseños de aplicaciones existentes. 
No creo que sea necesario explicar a que me refiero con aplicaciones complejas o capacitación. Si quizá un poco sobre a que me refiero con coaching. Cuando capacitamos, no nos interesa únicamente dar los cursos, sino hacer un seguimiento  con la gente una vez terminados. Lo que ofrecemos es realizar pair programming con los asistentes para que realmente apliquen lo que enseñamos. Esta es la única manera de agregar valor en la capacitación y que realmente la gente aprenda.
Seguramente también sea necesario explicar a que me refiero con "diagnóstico de código y diseño". Bueno, ustedes saben que hay muchas empresas que ofrecen consultoría para ayudar a mejorar los procesos de desarrollo, implementar un sistema de SCM, etc, pero pocas (sino ninguna) se mete a bailar con la más fea, donde realmente hay que moverse y demostrar experiencia y conocimiento; pocas se meten en el código y el diseño de un sistema existente y ofrecen un diagnóstico sobre cómo está hecho y cómo se lo puede mejorar. A esto es lo que apuntamos con este servicio, la idea es arremangarnos, ver el diseño y el código del sistema que les parezca importante y darles un diagnóstico del mismo. Este diagnóstico sería similar a lo que nos da un médico clínico cuando lo visitamos. Nos dice en qué andamos mal y qué deberíamos hacer para mejorar, etc; nosotros haremos lo mismo. Ver un sistema, analizar su diseño, cómo está codificado para luego generar un informe que indique su "grado de salud" (qué tan enfermo está, cuanto va a vivir, qué se puede hacer para alargar su vida, etc). Sinceramente no conozco empresa que ofrezca este servicio puesto que es muy difícil de realizar y hay que estar muy capacitado para obtener buenos resultados. Nosotros tenemos la capacidad y conocimiento para hacerlo y es un valor agregado muy importante que ofrecemos.
Bueno, basta de marketing por un rato.... Respecto del blog, voy a intentar postear más seguido ahora, aunque en realidad este cambio aún me está requiriendo más tiempo del que esperaba, imagino que cambirá en unas semanas. 
Por otro lado no quiero dejar pasar la oportunidad de agradecer y mencionar lo feliz que fue y es para mi trabjar en Mercap, he aprendido muchísimo a nivel técnico y humano, hemos tenido grupos de personas muy lindos y hemos logrado cosas que no he visto en ninguna otra empresa. De hecho, voy a seguir trabajando algunas horas en Mercap puesto que es una empresa que realmente amo, por la gente, por lo que hacemos y por lo que hicimos. Por eso quería agradecer también por este medio a Alejandro (dueño de Mercap) por la oportunidad que me da de seguir formando parte de la empresa y apoyarme en este nuevo emprendimiento.
En fin, ya cerrando y volviendo al marketing :-), si alguien está interesado es saber un poco más qué ofrecemos, tiene interés en conocer precios o contratar algún servicio, no deje de avisarme!

jueves, 19 de marzo de 2009

Ingeniero o Arquitecto

Son muchas las comparaciones que se hacen entre el desarrollo de software y la arquitectura o la construcción de edificios/casas/etc., de hecho el término "Arquitecto de Software" proviene de dicha disciplina. En este post voy a tratar de explicar por qué me parece que está mal utilizado este término (como muchas otras analogías que se hacen con esa disciplina). 
Este tema me vino a la cabeza luego de la clase de ayer en la UBA cuando conversábamos sobre qué significa que un modelo sea bueno. Recuerden que desde nuestro punto de vista, el software es un "modelo computable" de un dominio de problema de la realidad, por lo tanto debe cumplir con ciertas características para ser un buen software. Por un lado, debe cumplir con las mismas características con la que se juzga cualquier modelo, no solo los computables, esto básicamente significa que pueda representar correctamente lo que modela. Pero también existen características que debe cumplir por ser computable que tienen que ver con performance, uso de recursos, etc. 
Es interesante ver que generalmente en las carreras de sistemas nos enseñan como lograr buenas características computables de los programas que realizamos como que sean performeantes, etc., pero rara vez existen materias en donde nos enseñen a crear un buen modelo. Justamente este tema es el que ataca nuestra materia.
¿Qué tiene que ver todo esto con los arquitectos?, bueno, el rol que "actualmente" vela porque un sistema tenga buenos atributos computables, es lo que llamamos "Arquitecto". Es él quien debe asegurar que el sistema sea performeante, que escale bien, etc. Es el que se encarga de los temas de infraestructura del sistema.
Analizando un poco el mundo de la construcción de edificios/casas/etc., se puede ver que el encargado de asegurar que estas construcciones "no se caigan", que las estructuras sean las correctas, etc., o sea que todo lo relacionado con la infraestructura sea correcto, no es el arquitecto si no el ingeniero civil. De hecho, los planos de estructura, vigas, etc. deben estar hechos por un ingeniero civil y no por un arquitecto! El arquitecto generalmente se ocupa de hacer que el edificio/casa/etc. sea funcional, "lindo", aproveche bien los espacios, etc., todas características que no tienen nada que ver con la infraestructura en si... (Los arquitectos son también quienes generalmente llevan el proyecto adelante, pero ese es otro tema).
Por lo tanto, si lo pensamos detenidamente y no me equivoque en las responsabilidades del arquitecto y el ingeniero civil, utilizar la palabra Arquitecto dentro de nuestra profesión para indicar el rol de la persona que se encarga de definir la infraestructura del sistema parece ser equivocado, puesto que de donde proviene, esa palabra no es utilizada para dicho rol. El nombre utilizado para nombrar a la persona que cumple ese rol es Ingeniero Civil, por lo tanto sería mejor llamar al rol similar en el desarrollo de software  "Ingeniero", pero claro, dicho término tiene otra connotación en nuestra disciplina y está más relacionado con la persona que se encarga de construir un sistema, que el sea usable y por que no, llevar adelante un proyecto... ups! pero eso es lo que hace un arquitecto en el ámbito la construcción!
¿Me estoy perdiendo algo o realmente estas palabras están mal usadas en nuestra profesión? ¿No deberíamos llamar ingeniero al arquitecto de software y viceversa? ¿qué les parece?

jueves, 12 de marzo de 2009

Jornadas Agiles

El fin de semana pasado se realizaron unas Jornadas Agiles utilizando una organización muy interesante denominada Open Space.
La idea de esta técnica es que los participantes mismos decidan como será la jornada, decidan los temas y la agenda de cómo serán tratados. Inicialmente parece medio loco no?, algo que va en contra de la idea de tener todo organizado de entrada y bien definido, una característica que buscamos quizá por una cuestión de enseñanza y no por vivencias propias.
Les comento que la organización terminó siendo buenísima. La gente propuso los temas, la agenda se armó de manera colaborativa y no llevó más de 1 hora y media hacerlo con 100 persona y con 25 temas en total (creo). Pero lo más importante aún, desde mi punto de vista, es que no hubo exposiciones (salvo algunas como la mía que intenté hacerla más colaborativa y no lo logré) sino reuniones donde todos opinaron y colaboraron, lo cual generó muchas ganas de participar y un ambiente muy motivador... esta motivación me hace acordar a cuando se realiza pair-programming o sesiones de diseño grupales.
En fin, se utilizó una técnica que tiene mucho que ver con el espíritu ágil y que tenemos ganas de utilizarla por lo menos para un día en el congreso de Smalltalks de este año... así que vayan pensando temas!

Sesiones de Diseño II - TDD en Vivo

Ya vamos por la tercer sesión de diseño y realmente ha sido muy interesante. (Un amigo, JP, suguirió que debería llamar al post "TDD en Vivo", por eso lo adjunté al nombre).
Es muy interesante ver como cada grupo fue tomando distintos caminos y concentrándose en distintos problemas. Sin embargo hubo una característica común que permitió avanzar rápidamente en todos los casos y que soluciona el error principal que se cometió en la primer sesión: se empezaron a hacer test más sencillos y cortos. Fue muy interesante ver cómo al hacer tests más cortos se lograron mejores resultados y la gente se motivó mucho más. Aunque el avance esencial haya sido poco, el hecho de haber creado muchos tests y que den verde indudablemente afectó la moral del equipo. Es algo que Kent Beck ya había comentado cuando propuso esta técnica y que nuevamente estamos confirmando.
Por otro lado es muy interesante ver que los diseños en general no varían mucho, hay diferencias de nombres pero el problema está siendo resuelto de manera similar en la mayoría de los casos por los distintos grupos. También fue muy interesante ver con un grupo, como se entendió mejor y hasta se simplificó partes de código muy complejas simplemente factorizandolas con un extract method. Nuevamente hacer extract method y estar obligado nombrar (darle un significado) a un conjunto de colaboraciones  difíciles de entender o poco declarativas, ha demostrado ser una técnica muy valiosa.
Los mantendré informados!

The real crisis? We stopped being wise (Barry Schwartz)

Hace unas semanas hubo una charla muy interesante de Barry Schwartz donde comenta lo importante de ser "inteligentes" por más de que existan "métodos" o "procesos" que nos digan cómo tenemos que trabajar o actuar. Un charla imperdible:  http://www.ted.com/talks/barry_schwartz_on_our_loss_of_wisdom.html

miércoles, 4 de marzo de 2009

Lunes 16/03, 17:00 hrs: Defensa Tesis de Satori: un Ambiente de Aprendizaje de Objetos

Invitamos a todos a la defensa de la siguiente tesis de licenciatura, que
se llevará a cabo el Lunes 16 de Marzo a las 17:00 hrs, FCEyN, aula a
confirmar:

Título: "Satori: un Ambiente de Aprendizaje de Objetos"

Alumnos: Burella, Juan y Olivero, Fernando
Director: Hernán Wilkinson
Jurados: Máximo Prieto, Gabriela Arévalo
--------------------------------------------------------------------
Resumen
El paradigma orientado a objetos es actualmente uno de los paradigmas de desarrollo más utilizados en la industria y en la educación, hecho que determina la importancia de su enseñanza. Se puede afirmar que a pesar de haber transcurrido varias décadas desde su aparición, su enseñanza aún genera desafíos pedagógicos, los cuales se ven influenciados por condiciones externas que dificultan la generación de los resultados esperados. Estos inconvenientes no son inherentes al paradigma sino que, en la mayoría de los casos, son manifestaciones de las carencias que existen en las técnicas y herramientas utilizadas para enseñarlo. 

En este trabajo se han analizado varios ambientes, entornos y herramientas utilizados actualmente para su enseñanza, documentando los beneficios y déficits que poseen para cumplir con los objetivos que, según nuestro punto de vista, deben cumplir para tener éxito en la transmisión de este paradigma. Debido a los déficits encontrados, es que vimos la necesidad de crear un nuevo ambiente de aprendizaje, lo cual nos permitió simultáneamente detectar una serie de requerimientos que entendemos estos ambientes deben satisfacer.

Satori es por lo tanto el resultado de este trabajo de investigación y desarrollo, al cual lo podemos considerar un ambiente de objetos "puro". Éste, reifica cada uno de los conceptos básicos del paradigma e intenta reflejarlos de la forma más simple y directa posible. Incorpora, desde su concepción, características que pretenden promover la exploración y manipulación como metodología de adquisición de conocimiento y su modalidad de uso expresa la metáfora más importante del paradigma: objetos que colaboran entre sí enviándose mensajes. De esta manera los usuarios del ambiente pueden llevarse un panorama global de qué es el Paradigma Orientado a Objetos, reconociendo a los objetos y los mensajes como las entidades fundamentales.

El ambiente de aprendizaje de objetos Satori, ha sido además desarrollado en un sistema tridimensional colaborativo denominado Croquet. Esto permitió la representación en tres dimensiones de los elementos básicos del paradigma, ofreciendo por lo tanto herramientas de inspección y manipulación naturales, principios básicos del modelo pedagógico constructivista. Ofrece también todas las herramientas necesarias para la colaboración virtual entre personas, facilitando y permitiendo compartir el conocimiento adquirido durante el aprendizaje.

viernes, 27 de febrero de 2009

Sesiones de Diseño en Mercap

Esta semana retomamos en Mercap algo que llamamos "sesiones de diseño". La idea es juntarnos en grupos de 4 o 5 personas y resolver de manera completa un problema utilizando TDD, en vivo, con una máquina y proyector.
El motivo de hacer "coding" en vivo usando TDD en vez de solo diagramas en un pizarrón tiene que ver con el resultado que se obtiene en ambos casos. Hacer diagramas en un pizarrón está bien para hacer algo rápido y hasta a veces incompleto, pero como ya sabemos, cuando hay que programar la solución, hay que tener en cuenta todos los detalles, hay que formalizar lo que pensamos y es en estos casos donde se aprende más. Esto me hace acordar la frase "el diablo está en los detalles..." bueno, acá no es el diablo pero sí hay temas muy interesantes que surgen al hacerlo
Por cuestiones de responsabilidades no hacemos que estas reuniones duren más de una hora y media y tratamos de que los problemas no tengan que ver directamente con el negocio en el cual trabajamos para despejar un poco la mente de los problemas del día a día.
La verdad que es muy interesante hacerlo y muy enriquecedor. Desde el punto de vista pedagógico es interesante ver la mecánica de cada grupo, las ideas distintas y similares, etc. También vamos a comparar los resultados de cada grupo para aprender de las diferencias.
Hasta ahora la conclusión más interesante que saqué salió de un error que cometieron todos los grupos y fue que el primer test que hicieron fue muy complejo, no siguió el espíritu de TDD, de hecho llevó casi una hora escribirlo y en dos grupos no logramos hacerlo funcionar en el tiempo de la sesión. Pero también es verdad a que se debió por el enunciado del problema, en donde el primer caso planteado era complejo. 
Otra diferencia interesante fue como un grupo generalizó mucho más de entrada que el resto y fue justamente este grupo el que menos logró hacer finalmente, como era de esperarse.
Es bueno recordar que TDD nos pide hacer iteraciones chiquitas, tratar de tener un "verde" lo más rápido posible, y esto tiene que ver más que nada por una cuestión psicológica... ver que avanzamos nos mantiene contentos e inspirados.
La semana que viene veremos como sigue la cosa. Voy a tratar de seguir posteando las conclusiones que me resulten interesantes.

miércoles, 25 de febrero de 2009

Mock objects considered harmful (sometimes) [1]

Estuve leyendo un poco más sobre BDD y caí en estos artículos que explican como hacer BDD con RSpec en Ruby:

http://www.oreillynet.com/pub/a/ruby/2007/08/09/behavior-driven-development-using-ruby-part-1.html
http://www.oreillynet.com/pub/a/ruby/2007/08/30/behavior-driven-development-using-ruby-part-2.html

El segundo es más interesante que el primero. Si tienen un tiempo léanlo antes de seguir, por lo menos la parte final a partir de la cual crea la clase Game para representar el juego (Sección "Indroducing Mock Objects vis the Game Class"). Por favor, analicen críticamente el diseño que propone y los test que hace antes de seguir (por supuesto que pueden seguir leyendo si quieren, pero seguramente van a entender mejor lo que digo si lo leen).
Bueno, si lo leyeron y analizaron, deberían tener algunas críticas, ¿no?. Yo tengo un par que me parecen muy importantes, es más una hasta me hizo (y hace) dudar sobre la capacidad y experiencia de la persona que escribió pero voy a tratar de no ser muy duro puesto que es muy fácil criticar y muy difícil hacer. (Si llegaron hasta acá y se están preguntando que tiene que ver todo esto con el título, no desesperen, ya llega).
La primer critica que tengo es el acoplamiento que tienen los objetos instancias de Game[2] con la UI. Ver este acoplamiento me hace creer que la persona que escribió el artículo no conoce MCV (Model View Controller), el primer framework para desarrollar aplicaciones visuales (me atrevería a decir el primer framework) y del que se han derivado muchos patrones (si leyeron el libro de Design Patterns saben a que me refiero). 
Una de las reglas de diseño que  MVC "impone" es que el modelo nunca debe conocer la UI. La UI es un consumidor del modelo y el modelo únicamente provee comportamiento que luego las ventanas, views, etc. usarán para mostrarlo. En la solución planteada por G.Brown, objetos instancias de Game (que son objetos del modelo) conocen una UI, de la cual obtiene información como quienes son los jugadores o de cuantos puntos es el tablero. Este acoplamiento desde mi punto de vista es innecesario y puede ser resuelto fácilmente pasándole esa información a Game cada vez que se crea una instancia. O sea, la UI una vez que tiene toda la info y el usuario presionan el botón "Jugar" (o como se llame), crearía una instancia de Game indicándole quienes son los jugadores y cuantos puntos tiene el tablero, algo así:

playPushed
   ....
   game := Game playedBy: (Array with: 'Juan' with: 'Pepe') withSizeOf: 10.
   ....

De esta manera no existiría ningún acoplamiento entre Game y la UI, de hecho cualquier objeto podría crear un Game, como por ejemplo un test. Lo bueno de esta solución es que tampoco es necesario usar un mock para simular la UI (por no estar esta acoplada con Game), permite que nos concentrarnos en el modelo y que no tengamos que "adelantarnos" y "suponer" cual será el protocolo de la UI. So far, la primer crítica. Primera conclusión, seguir los principios de MVC! Segunda conclusión: El mock de UI no fue necesario
Paso a la segunda crítica: En el ejemplo del paso 15, se puede ver que se redefine el mensaje de construcción de instancia de Grid para devolver un mock que la simule. Según lo que comenta G. Brown, hacerlo evita que se tenga que usar Grid o crear instancias de esta clase "innecesariamente". No quiero sonar rudo, pero me parece un pésimo ejemplo y una muy mala conclusión. En primer lugar porque está rompiendo el encapsulamiento de Game al redefinir el comportamiento del mensaje de construcción de instancias de Grid, y lo está rompiendo porque sabe que Game "usa" Grid para representar el tablero. ¿Qué pasaría si Game decide utilizar otro objeto para representar el tablero? El test dejaría de funcionar. Claramente el hecho de que Game colabore con Grid es una decisión de Game que a nadie debería importarle y menos a un test!. Haberlo hecho hizo que el test se convirtiera en uno de "caja blanca", con todas las desventajas que sabemos que eso tiene. 
Lo mismo sucede con el mock que llama box_set y que utiliza para realizar movimientos por medio del mensaje #stub_move. Lo que está haciendo ahí en definitiva es simular lo que debería suceder en los objetos del modelo del juego, ¿pero para qué? ¿no se supone que estamos testeando el modelo? Además, si el mismo ya está hecho, ¿para qué lo simulamos? (además de romper nuevamente el encapsulamiento...). Devuelta, criticar es fácil, por lo tanto para no ser uno más del montón hay que ofrecer una solución, y para mi la solución acá es simple: ¡utilizar Game sin ningún mock! ¿Cuál es el problema de decirle a una instancia de Game que se unieron dos puntos tantas veces como sea necesario para crear el estado del juego sobre el cual se quiere testear (o empezar a testear) su comportamiento?
Bien, ahora si voy al título del post. Yo me pregunto, ¿G. Brown realmente cree que hacer esto está bueno o simplemente lo hace para utilizar (y mostrar como utilizar) mocks? Si el motivo fuese mostrar como utilizar los mocks, no me parece un buen ejemplo y hasta diría que es destructivo porque como ya sabemos, los seres humanos aprendemos de ejemplos y no quiero ni pensar todos los pobres programadores que están haciendo tests utilizando este ejemplo como guía. Si por el contrario lo hace así porque cree que es un buen diseño, mi pregunta es: ¿por qué cree que es un buen diseño? Una posible explicación es, como puse más arriba, que no conozca de MVC o de encapsulamiento, lo cual sería una lástima por todos los que lo leen; pero otra posible explicación, y es la que me motivó a poner el título del post y que veo muy a menudo últimamente, tiene que ver con la famosa frase que dice: "cuando lo único que conoces es un martillo, todos los problemas se parecen a un clavo". En este caso el martillo son los mock objects y que todo se parezca a un clavo son las soluciones que ofrece en su diseño usándolos. Lamentablemente en nuestra profesión es muy común ver como cuando aparece algo nuevo (o que parece nuevo) se lo empieza utilizar sin realizar el más mínimo análisis crítico de si se lo está usando correctamente; es nuevo, es cool, ¡usémoslo!
Eso es lo que veo muchas veces con los mocks, se los sobre-utiliza generando ejemplos perjudiciales y problemas de mantenimiento en los tests. En el ejemplo del mock para Grid: ¿qué pasaría con el test si se decide modificar el nombre del mensaje de creación de instancia de Grid? Claramente dejaría de funcionar y simplemente por el hecho de haber roto el encapsulamiento.
¿Cuándo utilizar un mock object entonces? Por lo menos para mi en este post me quedó claro que existen dos casos donde no hay que usarlos:
1) Para resolver problemas intrínsecos de diseño (como el acoplamiento de Game y UI)
2) Cuando se rompe el encapsulamiento (como el caso de Game y Grid)
En los tests, según mi experiencia, hay que usar mock objects para representar objetos que están por "afuera" del objeto que se está testeando y que este debe colaborar con para poder llevar adelante una responsabilidad. Pero mi consejo es, antes de usar un mock asegúrense que no pueden usar un objeto "de verdad"...

[1] Me imagino que todos deben conocer el famoso paper de Dijstra, "Goto considered harmful" que para algunos marcó el hito del inicio de la programación estructurada. De la misma manera que ya el "... driven development" está siendo sobre utilizado, me tomé el atrevimiento de sobre-utilizar el "... considered harmful" para llamar la atención. Espero por lo menos haber sido fiel al espíritu del artículo de Dijstra...
[2] Dicho sea de paso, Game no me parece un buen nombre para esta clase. Debería indicar por lo menos qué juego es.

viernes, 20 de febrero de 2009

Qi4j and state modeling

Un amigo me pasó este link sobre algo que están haciendo en Java: http://www.jroller.com/rickard/entry/qi4j_and_state_modeling
Es interesante, pero es algo que nuevamente refleja las complicaciones que tienen en Java para poder hacer cosas sencillas. 
Primero: el problema que tienen por usar POJOs (Plain Java Objects, lo que comúnmente hacemos en Smalltalk) puesto que lo escrito es muy distinto a lo que se "habla" en el dominio. Me alegro de escuchar ese argumento!, quiere decir que la gente está empezando a apreciar lo que venimos diciendo por años y es la importancia de hablar "el mismo idioma" con el experto del dominio, el problema es que no se dan cuenta que el "problema" está en la sintaxis y diseño de Java y que hay otros lenguajes, como Smalltalk, donde eso no sucede. O sea, el problema no está en usar Plain Objects, el problema esta en usar Plain Objects con la sintaxis de Java. Justamente debido a la sintaxis de Smalltalk es que a nadie se le ocurriría hacer algo como lo que está haciendo esta gente. 
Segundo: Habla de que quieren tener un lenguaje similar al del dominio del problema, pero sin embargo usar un keyword que seguro nadie del dominio entiende como @UseDefault. ¿Qué significa eso para un experto del dominio?, seguro nada
Tercero: Me vuelven loco los ejemplo pelotudos de uso de interface como este en donde extrae la propiedades de ser "nombrable" y crea la interface "Nameable". Y perdón por la palabra pelotudo, pero la verdad este tipo de ejemplos lo único que hacen es complicarle la vida al que aún está aprendiendo. Uno de los motivos es porque esto sucede al final de un desarrollo, no mientras se está desarrollado y entendiendo el dominio. De este ejemplo se puede sacar la conclusión de que cualquier cosa que tenga sentido se puede poner en una interface por que si, sin analizar que hacerlo puede agregar complejidad al diseño! ¿qué otra cosa que no sea un ser humano puede tener nombre y apellido? Nada! Entonce, ¿para que factorizarlo así?. Por otro lado, el motivo por el cual dice que es bueno hacerlo es porque ahora podrán preguntar si "x instanceOf Nameable"... parece que de polimorfismo poco esta persona. Justamente preguntar por el tipo de un objeto es el claro ejemplo de que aún están pensado en "estructurado" y no en objetos. No más palabras sobre esto porque me está subiendo la temperatura
Tercero: Dice que cuando modelan le gusta hablar de propiedades, relaciones, etc., pero en ningún momento veo que hable de comportamiento! lo más importante! Si solo le interesa propiedades y relaciones, ¿por qué no hace un DER?. Nuevamente, un ejemplo de que no lograron entender el paradigma de objetos y están usando técnicas de otros paradigmas. 
Cuarto: No entiendo por qué separan Entity de Value. Es algo que ya lo vi varias veces y el motivo es que los Values son inmutables, las entities no. Nunca escuche esta separación en filosofía ni en epistemología ni en nada similar, solo en nuestra profesión y por lo tanto me parece que es un problema del dominio computable... si es así, ¿por qué confundirlo a experto del dominio con esto?.
Quinto: El ejemplo del método listName() en la sección Privitanzing State me mató! jaja. Más me mató la frase siguiente: "Heat huh?". ¿Neat?, yo diría "a mess man!" a mess que rompe el encapsulamiento y que para perdir un nombre tengo que envier el mensaje "name()" y luego "get()"! o sea, primero tengo algo que no es lo que quiero (seguramente será un Value o Entity) y luego le pido su "valor" que es lo que realmente quiero... ¿no debería ser al revés en todo caso para que sea más simple?... en fin.

Algo que me gusto, es que como vine escribiendo últimamente, evitan usar nil (o null en java). Parece que esta idea está empezando a tomar forma simultáneamente en otras comunidades lo cual es bueno. Otra cosa interesante es que reifican las relaciones (algo esperable también por la orientación al paradigma relacional que tienen), algo que también veo como importante pero que lamentablemente nunca tuve la oportunidad de probarlo seriamente.
Conclusión, que lástima ver el tiempo que se pierde en tratar de resolver problemas no esenciales (reinventar la rueda pinchada como dice Alan Kay) creados a partir de decisiones de diseño incorrectas (en este caso decisiones del diseño del lenguaje Java).

miércoles, 18 de febrero de 2009

Tip 3: Objects must be valid since its creation time

Este tip de diseño del que hablé en mi presentación de Smalltalks 2008 es muy importante de mi punto de vista. La idea que trata de transmitir este tip es que cuando se crea un objeto, el mismo debe ser válido. Hay motivos teóricos y prácticos para demostrar por qué es un tip importante. Voy a empezar por el teórico.
Si están de acuerdo con la visión de que un objeto es una representación de un ente de la realidad, es muy importante preguntarse a partir de qué momento ese objeto representa el ente en cuestión. Y la respuesta sería (rápidamente) ¡cuanto antes mejor!, sino mientras tanto tendremos un objeto que no representa nada, un objeto que no puede cumplir con sus responsabilidad, un objeto que puede traer problemas. Este tip se relaciona bastante con el anterior en el cual comentaba la importancia de identificar claramente cuál es la identidad del ente representada en el objeto puesto que el objeto puede representar el ente una vez que la identidad del mismo es modelada por el objeto. Esto ya parece un trabalenguas. Voy a dar un ejemplo para tratar de transmitir la idea más claramente por lo que ahora nos iremos metiendo en los motivos prácticos. 
Supongamos que tenemos que modelar una tasa de interés. En cualquier libro financiero verán que una tasa de interés es representada por un porcentaje durante cierto tiempo. Es común por lo tanto encontrarse con tasas de interés del 10% anual, o del 5% mensual. Una tasa de interés del 10% anual significa que si invertimos nuestro capital por un año, al finalizar el año recibiremos un 10% de nuestro capital como "premio" por haber hecho la inversión (estoy suponiendo que no hay pago intermedios, etc). La tasa de interés es presentada algebraicamente como la división del porcentaje por el tiempo. Esto se puede ver claramente cuando escribimos la fórmula de interés:

interés = capitalInvertido * tasaDeInterés * tiempoDeInversión  donde tasaDeInterés = porcentaje / tiempo 
por lo tanto:
interés = capitalInvertido * porcentaje / tiempo * tiempoDeInversión  

Por lo tanto si capitalInvertido es 100 $, la tasa es 10% anual e invertimos por 2 años, tendríamos:

interés = 100 $ * 0.1 / 1 año * 2 años --> años con años se simplifican y tenemos = 100 $ * 0.1 * 2 = 20 $

Volviendo a nuestro modelo, podemos usar la clase "InterestRate" para modelar tasas de interés, la cual sabrá responder mensajes de creación de instancia como #of: aPercentage every: aTimeMeasure, o mejor aún #yearlyOf: aPercentage. Veamos un ejemplo en código:

InterestRate of: 10 % every: 1 year --> Devuelve una tasa de interés del 10% anual
InterestRate yearlyOf: 10% --> Hace lo mismo que la colaboración anterior

Ahora bien, este tip dice que el objeto que los objetos deben ser válidos desde el momento que se crean, esto significa para este ejemplo que cuando se crea una tasa de interés la misma debe ser válida. ¿Qué tasa de interés no sería válida? (una pregunta medio retorcida puesto que en realidad si no es válida no existe como tasa de interés, pero creo que se entiende no?). Claramente una tasa de interés no válida es aquella cuyo lapso de tiempo sea 0 (0 años, 0 meses, 0 días, etc. ¿recuerdan mi comentario sobre la igualdad del 0 con las medidas?) o cuyo porcentaje o tiempo sea negativo. El primer caso se puede deducir fácilmente puesto que de lo contrario el valor representado por una tasa de interés (que es la división del porcentaje por el lapso de tiempo como vimos más arriba) no se podría calcular por estar dividiendo por 0. El segundo tiene que ver más que nada con reglas de negocio, no existen tasas de interés negativas o con tiempos de inversión negativo! no tiene sentido. 
Esto significa que en el mensaje de construcción de instancia de "InterestRate" tenemos que asegurar estas tres pre-condiciones. La manera en que nosotros lo implementamos es la siguiente:

InteresRate class>>of: aPercentage every: aTimeMeasure

self assertPercentageIsPositive: aPercentage.
self assertTermNotZeroOrNegative: aTimeMeasure.

^self new initializeOf: aPercentage every: aTimeMeasurement
Se puede ver claramente que lo primero que se hace es validar que las pre-condiciones de existencia (y fíjense que ya no dije validez) de una tasa de interés se cumplan. Si la validación no es exitósa se generará una excepción, que en nuestro caso será una subclase de InstanceCreationException, indicando que pre-condición no se cumplió.
Fíjense que por haber hecho esto al momento de crear una tasa de interés, es imposible tener tasas de interés inválidas. El concepto de tener objetos inválidos desaparece y de repente solo tengo o no tengo dichos objetos, lo cual podríamos decir que se condice con la realidad puesto que en la misma no existen tasas de interés inválidas, nunca un banco daría una tasa de interés con tiempo 0 o negativo, es impensable. Uno podría decir que -10% anual es una tasa de interés, lo podría escribir en un pizarrón o aún en este blog, pero llamarla tasa de interés es un abuso y hacerlo es simplemente una ilusión que podemos tener puesto que realmente no es una tasa de interés y un experto del dominio nos lo diría de entrada.
El problema de tener objetos válidos o inválidos tiene que ver más que nada con un problema computacional debido a que alguien (persona o computadora) en algún momento debe ingresar los "datos" para crear la tasa de interés. Esto implica que existe un tiempo en el cual se ingresan "datos" y que por lo tanto deben estar en "algún lado" en nuestro modelo durante ese tiempo y también implica que esos datos pueden estar incompletos. Pero entonces ese problema no corresponde ser resuelto por el objeto del modelo, en este caso "InteresRate", sino por aquel que se encarga del ingreso de datos. 
Al haber hecho que una tasa de interés no se pueda crear si no es válida, ya no solo podemos asegurar que el usuario nunca creará tasas inválidas sino también que ningún programador lo hará!, si lo hace enseguida nuestro modelo le indicará que se equivocó, lo cual es buenísimo y tiene que ver con otro tip que veremos más adelante que se llama "Fail fast". Otra ventaja es que tenemos las famosas validaciones de negocio, o mal llamadas reglas de negocio en un solo lugar y no dispersas por cualquier lado (la UI, una interfaz batch, etc). Siempre que se cree una tasa de interés, venga de donde venga, la UI, un programador, otra computadora, etc, se realizará este chequeo que nos asegura que nuestros objetos siempre serán.... válidos (pensé en terminar la frase en serán, puesto que si no lo son, no existen que es lo mismo según mi punto de vista a que no son válidos; es como pensar que la palabra "válido" desaparece de mi idioma, deja de tener sentido).
Ustedes podrán preguntar cómo representamos entonces aquellos casos para los cuales la información ingresada es incompleta o no correcta, bueno, por medio de un objeto para tal fin! Por ejemplo, si aplicamos el mismo concepto para un "Deal" podríamos tener una intención de deal (DealIntention) para representar aquel que aún no se sabe si es válido. Esto puede parecer como que generará una explosión de clases pero la realidad no es así. En nuestro caso en lo que respecta a la UI, son los mismos widget o controllers los que se encargan de contener esa información inválida (Este blog ya es muy largo, pero cuando pueda voy a comentar como funciona la UI que hicimos para XTrade porque me parece muy interesante)
Esta característica de tener objetos solo válidos se potencia aún más cuando dichos objetos son inmutables, como el caso de la tasa de interés, o fechas, o medidas, etc. puesto que no habrá manera de que dicho objeto pase a ser inválido con el transcurso del tiempo. Si el objeto es mutable, podemos aplicar lo que mencione y detallaré más adelante como sincronización por copias. 
Analizando aún más este tip, veremos que para objetos inmutables estas pre-condiciones se fusionan con el concepto de invariante. Hemos logrado poner en un solo lugar las pre-condiciones de varios métodos y la invariante de un objeto, lo cual me parece muy interesante por haber simplificado esta representación.
Algunos detalles de implementación adcionales:
1) Todos los mensajes de creación de instancia deben en definitiva terminar cayendo en la evaluación de un único método de creación de instancia que es además es que realiza todas las aserciones. Para nuestro ejemplo, #yearlyOf: debería estar implementado así:

InterestRate class>>yearlyOf: aPercentage

   ^self of: aPercentage every: 1 year

2) Debería haber un solo mensaje de inicialización que es enviado desde un único método de creación de instancias que es el que realiza las aserciones. Esto implica que el mensaje #new enviado a self se realiza en un solo lugar.
3) Se puede reificar aún mejor el concepto de pre-condiciones para luego hacer meta-programación con las mismas y por lo tanto generar documentación automática (para interfaces por ejemplo) o test automáticos como presentó Meyer la semana pasada
Bueno, suficiente por ahora, no tengo ni tiempo de releer lo que escribí así que espero que se pueda leer bien! 

Behavior Driven Development

Estuve leyendo sobre una "nueva técnica" (si, otra más!) para desarrollar software. Es una técnica y no una metodología por lo tanto no es algo pesado ni complicado. En realidad es una técnica que según el autor trata de resolver algunos problemas que se encontró mientras enseñaba y usaba TDD. 
El artículo donde describe la técnica como introducción es interesante. La técnica se llama Behavior Driven Development y se puede leer una introducción acá.
No la he probado aún pero hay varias cosas que comenta que comparto y que veo que tan equivocados no estábamos cuando empezamos allá por el 2000 o 2001 a utilizar TDD como técnica de desarrollo para luego darnos cuenta que era más que una técnica de testing. Parece ser que ya varios se dieron cuenta de esto y el cambio propuesto por Dan North y hasta las palabras que usa, como "Behavior" me recuerda mucho a lo que siempre estuvimos enseñando.
Espero que disfruten del artículo y si alguien tiene alguna experiencia usándolo por favor comente que le parece. Según tengo entendido está teniendo bastante actividad en la comunidad de Ruby.

lunes, 16 de febrero de 2009

Bertrand Meyer en Argentina

La semana pasada tuvimos el honor de que nos visitara Bertran Meyer. Dio dos charlas en Buenos Aires, una en la UTN, organizado por los Seminarios Athena y otra en la FCEyN de la UBA organizada por Marcelo Frías. 
La primera (dada en la UTN) se trató sobre su punto de vista sobre como enseñar el curso de introducción a programación en estos días. En la misma comentó por qué los métodos más usados no eran de su agrado (programming in the small, teach API, just formal programming, teach a language, just functional programming) y cuales utiliza él. Uno de los temas que recuerdo como interesante era el hecho de hacer que los alumnos empiecen a programar con un framework ya creado para que aprendan a usar primero para luego aprendan a hacer. Me hizo recordar a lo que hacemos nosotros cuando les damos Smalltalk en POO, primero lo empiezan a usar y luego lo tienen que ver en más detalle y hasta modificar en DAO. De los 26 temas que comentaba como importantes para enseñar solo hay 16, que para aquellas personas que conocemos la bibliografía de Meyer no son nada novedosas, ej. Design by Contract, OO, etc. Me parece que lamentablemente el tema no fue el más apropiado para la audiencia porque la mayoría eran estudiantes y no profesores!, además mostró un ejemplo que usan en la primer clase de la materia donde crean la clase Turist que subclasificaba Preview (el framework que usan es para mostrar ciudades y rutas), en la cual el objeto París sabe responder show, Louvre sabe responder highlight, etc. responsabilidades que no le daría a esos objetos, o nombres de objetos que no usaría en todo caso.
La charla en la UBA se trato sobre testing automatizado. Básicamente lo que hacen es generar objetos random con una heurística determinada para algunos tipos de objetos y luego enviar mensajes a dichos objetos (siempre y cuando los mismos cumplan las pre-condiciones) y ver si hay post-condiciones o invariantes que no se cumplan. Si fuese así, se detectó un error. Mostró una estadística en la cual el testing manual encontró 14 errores y el automático 9. Había errores automáticos no encontrados manualmente y viceversa por supuesto. Le pregunté sobre la cobertura que ofrecía esa herramienta y se armó un gran revuelo puesto que dijo que la cobertura no indicaba nada, cosa que concuerdo a medias. Si hay un 20% no cubierto, uno sabe que debe testear ahí. Si tengo un 100% de cobertura por supuesto que no asegura que haya errores puesto depende de con que conjunto de "datos" se hizo la cobertura. En conclusión, no sacaron estadísticas de cobertura. Le pregunté si habían medido el tiempo que llevó encontrar lo errores manuales y los automáticos, dijo que no. Le pregunté si había pensado en utilizar mutation testing para modificar métodos y validar contratos y viceversa, modificar contratos y ver validar código y respondió que no "entendía mutation testing". Esta charla fue más interesante desde mi punto de vista que la anterior. Lástima que a veces hablaba mucho sobre ciertos temas en los que se perdía el núcleo de lo que quería decir.
Por supuesto, no dejó de publicitar su libro que está por salir "Touch of class ..." y Eiffel.

viernes, 13 de febrero de 2009

Igualdad de Objetos II

Algo que me olvidé de comentar en el post sobre Igualdad de Objetos, es que como el mensaje #= debe devolver true si los dos objetos representan el mismo ente su implementación siempre será verificar si aquellos colaboradores internos que representan la identidad del ente en el objetos receptor del mensaje es igual a los colaboradores internos que representan la identidad en el objeto pasado como parámetro. En pocas palabra, asegurar que ambos "identifican" al mismo ente.
En el caso de Date, esto será verificar que el año, mes y día sean iguales (si esa es la representación que se decidió usar para Date).
Una consecuencia interesante en identificar que colaboradores internos representan la identidad del ente es que esto se podría hacer automáticamente lo mismo que una implementación default del mensajes hash, como también asegurar que dichos colaboradores no pueden cambiar.

nil (pongo algo más para que no parezca un error :-)

Cuando escribí el título de este post era solo "nil", pero imaginé que alguien podría pensar que sucedió algún error y que por eso aparece solo "nil" en el título. ¿No les pasó eso miles de veces en los distintos sistemas que hicieron? Yo lo vi mucho, por eso modifiqué el título para no crear esa confusión :-) (por supuesto eso implicaría que Blogger está escrito en Smalltalk, cosa que sabemos que no, pero en fin, me pareció una caso curioso)
Ahora si, lo importante del blog. En las charlas que dio Meyer, comentó que en Eiffel pueden ahora asegurar que nunca se envía un mensaje inválido a nil de manera estática,  que él representó como que "x.f con x en null es detectado estáticamente" (1). Lo que me pareció curioso de ese comentario es que aún existan personas que estén pensando en ese problema cuando la solución es bien sencilla! y es simplemente no usar nil! pero claro, todos lo siguen haciendo por una cuestión histórica y porque la mayoría de los ejemplos que vemos cuando estamos aprendiendo lo hacen. 
Cuando le comenté a Bertrand que nosotros no tenemos el problema del nil (o null para él) puesto que no usamos nil para nada porque tenemos es una regla de programación que nos pide crear todos los objetos correctamente de entrada y representar la ausencia de algún objeto con un null object, me parece que lo sorprendió un poco. Inmediatamente me preguntó "cómo representábamos el fin de una lista encadenada por ejemplo", a lo que respondí que simplemente reificaríamos(2) el concepto de fin de lista y utilizaríamos un objeto especial para tal fin, pero no nil. Mi sensación es que se quedó pensando, por lo menos ahí terminó esta parte de la conversación.
Más allá de esta anécdota reciente con Meyer, les quería transmitir nuestra experiencia respecto de este tema y de que se puede vivir sin el objeto nil y conviene hacerlo por ser la fuente de innumerables dolores de cabeza. No se dan una idea cómo se simplifica la codificación creando objetos correctos de entrada, sincronizando con copias, evitando usar nil... Piensenlo por un minuto y traten de imaginar cuantos errores desaparecerían, cuantos "doesNotUnderstand" o "null pointer exception" dejarían de aparecer si nil o null no existieran... seguramente sientan que son muchos.
Les comento otro detalle anecdótico que me parece al mismo tiempo importante y en sintonía con este consejo. Hoy cuando leía unos blogs me encontré con esta charla que dará Hoare en QCon: "Null References: The Billion Dollar Mistake". Según lo que interpreté de su abstract y título, hablará justamente de que la existencia de null references fue un error gravísimo, y que sólo lo creó en ALGOL W por ser un feature fácil de implementar.... otro feature fácil de implementar que trae dolores de cabeza, y del cual coincido plenamente con Hoare, es realmente un "Billion Dollar Mistake".
(1) Después cuando nos comentó como lo hacían durante la cena, mucho no le entendí pero me pareció muy complejo.
(2) Digo reificaríamos y no reificamos porque nunca tuvimos que crear una lista encadenada en Smalltalk :-)

martes, 10 de febrero de 2009

Igualdad de Objetos

En el post anterior hable sobre el hecho de identificar correctamente la identidad de un objeto y sobre el verdadero significado del mensaje #==, que en definitiva verifica si dos objetos son idénticos, puesto que dos objetos pueden no ser idénticos pero estar representando el mismo ente.
Como bien comenta Gabriel (Gaboto) en un comentario, puede suceder que existan dos objetos representando el mismo ente (su ejemplo me confundió un poco) y debemos tener alguna manera de identificar dicha situación. Un ejemplo claro se da en Smalltalk con la clase Date, donde dos instancias distintas (o sea no idénticas) pueden representar el mismo día (por ejemplo hoy). 
El mensaje que utilizamos para saber si dos objetos están representando el mismo ente es el mensaje #=, más conocido como "igual". Su semántica es devolver verdadero si dos objetos representa el mismo ente, lo que en lenguaje vulgar o natural decimos "si son iguales"... claro está, acá empieza la confusión. Son iguales por estar representando al mismo ente (por ejemplo el día de hoy, que es idéntico a si mismo) pero no son el mismo objeto necesariamente y por no lo tanto no idénticos entre sí.
Por lo tanto el mensaje #= toma una nueva dimensión y para mí es más claro decir que este mensaje "devuelve true si dos objetos representan el mismo ente", puesto que definir qué significa "si son iguales" es bastante problemático como veremos más adelante. Sería menos confuso usar otro mensaje para esta semántica, por ejemplo #representsSameEntityAs:, pero bueno, ya estamos muy acostumbrado al otro.
El motivo por el cual dos objetos pueden representar el mismo ente se debe únicamente a problemas computacionales. En el caso de la clase Date esto se podría solucionar teniendo una cache de fechas creadas y siempre devolver el mismo objeto para la misma fecha, una implementación similar a la de Symbol, sin embargo hacer esto puede no tener sentido por poder ocupar más memoria o ser más lento que la implementación actual al tener que buscar el objeto en esa cache. En definitiva, si existe un solo objeto para representar un ente o si existen varios es una decisión implementativa y es por ello que siempre hay que utilizar el mensaje #= para saber si dos objetos representan el mismo ente (si son iguales) y sólo utilizar el mensaje #== para dominios computaciones, donde realmente nos interese saber si estamos hablando del "mismo objeto", del objeto que ocupa la misma zona de memoria.
Ahora bien, si se decide que puede haber más de un objeto para representar el mismo ente como el caso de Date, hay que asegurar que esos objetos sean inmutables porque sino estaremos en graves problemas puesto que podrían existir representaciones desincronizadas del mismo ente (creo que a esto apuntaba Gaby con su ejemplo). Imagínense dos objetos representando la misma cuenta corriente donde el saldo en uno es distinto al saldo en otro y donde claramente solo uno de ellos representa bien el ente en ese momento por tener el mismo saldo de la cuenta (por supuesto que también podría suceder que ninguno represente el ente por tener los dos el saldo incorrecto...). En definitiva, un grave problema.
Si no es factible que dicho objeto sea inmutable, hay que asegurar que exista una única instancia "master" para representar el ente y el resto sean solo "representaciones temporales" que tendrán un propósito determinado y esporádico. Un ejemplo de este caso es lo que en mi presentación de Smalltalks mostré como "sincronización" de objetos que comentaré en más detalle más adelante, pero que básicamente consiste en sincronizar la copia "master" con la "termporal" de una vez, con el envío de un solo mensaje. Por supuesto que acá nos metemos con problemas transaccionales, pero por ahora también los esquivaré.
Otro tema interesante que se desprende de este es definir exactamente que significa "que sean iguales". Por ejemplo, 0.5 y 1/2 ¿son iguales?. Ambos representan la misma cantidad, la mitad de algo, por lo tanto desde el punto de vista numérico nos interesa que sean iguales, pero también son distintos. Por ejemplo, desde el punto de vista implementativo uno será representado como un número de punto flotante y el otro como fracción. Mismos ejemplos se pueden dar para medidas equivalentes representadas por distintas unidades. Más interesante aún es pensar si 0 es igual a 0 metro, 0 litro, 0 pesos, etc. No voy a ahondar mucho en este tema ahora, pero la verdad es que sí, todos estos objetos representan lo mismo, o sea nada en cantidad y por lo tanto "son iguales". A muchos esta afirmación les parecerá muy loca pero la explicación matemática es clara. Una medida es la representación de la multiplicación de un número por una unidad, por lo tanto 0 metro es lo mismo a decir 0*metro, que algebraicamente es 0 de la misma manera que es 0 la expresión algebraica 0*A. Algunos denominan esta situación como "el cero polimórfico". Ahora bien, todas estas medidas están midiendo cosas distintas y sería interesante poder determinar esa situación, ¿cómo lo hacemos si devuelven true cuando se les envía el mensaje #=? (lo mismo para 1 metro y 1000 milimetros o 100 centímetros, etc). En Mercap estuvimos discutiendo mucho sobre esta situación, discusión en la que también participó Máximo Prieto y que fueron muy interesantes y acalorados (por qué ocultarlo). Llegamos a la conclusión que como en matemática, debería existir la posibilidad de definir distintas categorías de equivalencias, de la cual la igualdad es simplemente una de ellas. Si esto tiene sentido entonces el mensaje #= sería una implementación de una categoría de equivalencia determinada y podrían existir distintos mensajes para implementar las otras... suena interesante no? ¿Cómo se definiría una categoría o cuál sería su significado? Dependerá de lo que queramos representar, es una decisión totalmente arbitraria... aunque quizá suene mejor consensuada, consensuada con una comunidad o grupo...
En fin, si les interesa este tema avisen, da mucho para discutir y charlar.

viernes, 16 de enero de 2009

Tip 2: Clearly identify what represents the identity of an object

En el primer Tip (si ya se, lo escribí hace mucho...) comenté sobre lo interesante que es tener como objetivo lograr un isomorfismo entre los objetos de mi modelo y los entes del dominio del problema.
Además, como ya puse en otros posts, no hay que perder de vista que los objetos representarán la esencia de dichos entes mediante su comportamiento, no estructura, relaciones, etc.
Sin embargo hay otro punto del que siempre hablamos y es la identidad. La identidad es la que permite identificar únivocamente un ente. Todo ente posee una identidad y por lo tanto el objeto que lo representa en nuestro modelo también debe tener una. Sin embargo hay una gran confusión entre lo que es identidad, lo que algunos llaman "estado de un objeto" y lo que es la identidad del objeto. Voy a empezar por este último.
Un objeto, por ser a la vez un ente de la realidad (además de estar representado otro) también posee una identidad. Esa identidad en un ambiente de objetos está generalmente implementada por medio de la posición de memoria que ocupa el objeto. Es lo que hace que ese objeto sea único, no puede haber otro objeto en la misma zona de memoria que otro. 
En Smalltalk, se puede saber si dos objetos ocupan la misma zona de memoria usando el mensaje #== Este mensaje tiene por propósito verificar si dos "objetos" son identicos, o sea, si ocupan la misma zona de memoria pero no puede decir nada sobre la identidad del ente que representan dicho objetos. En rigor de verdad, si dos objetos son idénticos significa que están representando al mismo ente y por lo tanto representan la misma identidad del ente, pero si dos objetos no son identicos no se puede asegurar que estén representando a entes no idénticos. Es acá donde se ve claramente la diferencia entre identidad de objeto e identidad de ente. Un ejemplo lo va a aclarar.
En Smalltalk existe la clase Date, pero si evalúo dos veces la siguiente colaboración "Date today" obtengo dos objetos que no son idénticos pero que sí representan al mismo ente, el día de hoy. Por lo tanto no hay que confundir identidad de objeto que viene dado por el ambiente de objetos de identidad del ente que la tenemos que definir nosotros en nuestro modelo.
¿Cómo representamos la identidad del ente? Dependerá del ente que estemos modelando o la implementación que utilicemos. Por ejemplo, los SmallInteger en Smalltlk nos dan la sensación de ser únicos (o sea, de que hay uno y solo un objeto por cada uno de ellos). A nivel implementativo sabemos que no es así, pero es un claro ejemplo donde la identidad del ente esta asociada a la identidad del objeto. Otro ejemplo son los objetos instancia de Symbol, no puede haber más de un objeto para el mismo símbolo que representan de la realidad. Como vismo, no sucede así con las fechas y con otros objetos como aquellos instancias de Time, Point, etc.
¿Qué es lo que representa entonces la identidad de los entes para estos objetos? Siempre es un conjunto de colaboradores internos (variables de instancia) que cumplen ese propósito. En el caso de Date, dependiendo de la implementación serán el número de día, mes y año. En el caso de Point serán esos objetos que representan la coordenada x e y. 
Una característica interesante que tenemos que cumplir con estos colaboradores que representan la identidad de los objetos es que no pueden cambiar, puesto que si lo hacen el mismo objeto estaría en un instante de tiempo representando un ente y luego otro, sería un objeto con "problema de personalidad" no les parece?. Es por ello que es fundamental entender bien qué colaboradores internos representan la identidad de un objeto puesto que ellos no pueden cambiar. Hay otras implicancias interesantes de este hecho, la primera es que el mensaje #= del objeto devolverá true si los colaboradores internos que representan la identidad del ente son iguales. Esto significa que si queremos saber si dos objetos representan el mismo ente, debemos usar el mensaje #=, es este mensaje el que nos dirá si dos objetos representan a un ente idéntico o en otras palabras, a exactamente el mismo ente. Moraleja, no confundir identidad de objeto de identidad de ente y por lo tanto nunca, pero nunca usar el mensaje #== en un modelo a menos que ese modelo esté tratando con un dominio de problema computacional y donde realmente se tenga por intención saber si se está tratando a exactamente "el mismo objeto" sin importar si además si representan exactamente el mismo ente.
Esto que estoy comentando les debería disparar una nueva explicación de porque el hash debe estar relacionado con el #= y por qué cuando un objeto cambia los colaboradores que representan la identidad cambia su hash que termina produciendo un descalabro importante en aquellos objetos que dependen de la invariabilidad de esta característica.
También esto tiene que empezar a dispararnos la idea de que hay colaboradores internos que representan la identidad del ente que modela el objeto y otros que no. ¿qué características pueden tener estos otros? ¿pueden cambiar? ¿que representan?. Son muchas preguntas, no tengo una respuesta 100% segura, pero en la mayoría de los casos me parece que dichos colaboradores existirán por motivos implementativos como por ejemplo tener un cache, poder acceder más rápido a un objeto, etc. y no a cuestiones esenciales del dominio de problema en si, pero no lo puedo asegurar.
Otra pregunta interesante para hacerse es, ¿debería el lenguaje ofrecerme herramienta particulares para hacer uso de esta diferencia? Por ejemplo, que defina una clase de esta manera:
Object subclass: #Point
   identityDefinedBy: 'x y'
   instanceVariableNames: ''
   ... etc.
Al hacerlo, automáticamente el ambiente podría asegurar que la identidad no puede cambiar, y algo más interesante aún que la misma se defina en el momento de crear el objeto, algo que veremos en el próximo tip.... mientras tanto tiro la idea, yo aún no tengo un posición tomada.