martes, 27 de mayo de 2008

Ejemplos de buena implementación

En la materia de POO, uno de los ejercicios que tienen que hacer es implementar un modelo de medidas y otro un autómata finito determinístico y no determinístico. Son ejercicios muy interesantes y que dan buenos ejemplos de problemas de modelado e implementación.
A continuación transcribo alguna de las cosas que les comenté por mail a los alumnos y que me parecen interesantes en general:

1) En el ejercicio de las medidas, la implementación del mensaje #*, que se puede utilizar para hacer cosas como: "diezMetros * 5" o "diezMetros * dosMetros", no utiliza polimorfismo sino un "if" para solucionar el problema de multiplicar por un número o por una medida. Por ejemplo:

Measure>>* aMeasure

^aMeasure class = self class
ifTrue: [ ... multiplicación de medidas, en muchos casos puesto el código acá directamente en vez de factorizarlo en un mensaje ]
ifFalse: [ ... multiplicación por un número ]

Esta solución adolece de varios problemas:
a) Restringe la solución a multiplicar medidas y cualquier otra cosa, asumiendo siempre que la otra cosa va a ser un número, lo cual está implícito y por lo tanto puede generar errores en la evolución del modelo. Para evitar este problema de evolución, una correcta solución sería:

^aMeasure class = self class
ifTrue: [ ... multiplicación de medidas, en muchos casos puesto el código acá directamente en vez de factorizarlo en un mensaje ]
ifFalse: [ (aMeasure isKindOf: Number)
ifTrue: [ ... multiplicación por un numero ]
ifFalse: [ Exception!!! -> El modelo no está preparado para multiplicar por cualquier otra cosa ]

b) La opción anterior, por supuesto que sigue estando distante de una buena solución, por el motivo que comente en el punto a (que restringe la multiplicación a lo que conocemos ahora que se puede multiplicar), pero también porque queda claro si nos fijamos con cuidado que el colaborador externo aMeasure no es un buen nombre, porque si es una medida, cómo puede ser un Number?. Esto tiene mal olor. Es este caso puede ser interpretado como que falta una abstracción que permita "juntar" el concepto de medida y de número en algo más abstracto, que en el caso de VisualWorks se llama ArithmeticValue. Por lo tanto, Measure debería subclasificar ArithmeticValue (igual que Number) y el colaborado externo se debería llamar "anArithmeticValue" en vez de aMeasure

c) Para no restringir la multiplicación a medidas y números, hay que utilizar la técnica que se denomina "double dispacth". La idea de esta técnica es que las decisiones la tomen los objetos enviándose mensajes en vez de los programadores por medio de un "if". La solución correcta para este caso sería:

Measure>>* anArithmeticValue

^anAritmeticValue multiplyMeasure: self

Measure>>multiplyMeasure: aMeasure

"En este caso estoy seguro que aMeasure es instancia de Measure, puesto que el mensaje lo envió una Measure"
^... (multiplicación de medidas)

Number>>multiplyMeasure: aMeasure

"Soy un número y me piden multplicar aMeasure por mi"
^Measure amount: aMeasure amount * self unit: aMeasure unit (o algo así)

Esta solución erradica el if y está preparada para el día de mañana si alguien observa que se puede multiplicar una medida por algo que no sea ni una medida ni un número... algo quizá medio difícil de ver en primera instancia, pero la puerta queda abierta para esa posibilidad (algún loco le gustaría poder multiplicar por un punto por ejemplo. Para el caso de VisualWorks no parece loco hacerlo puesto que Point subclasifica ArithmeticValue como también lo hace Complex)
Fijense que también el código queda automáticamente factorizado y en el caso de querer multiplicar por algo que no tiene sentido, por ejemplo un punto, automáticamente se genera un doesNotUnderstand y no hay que explícitamente manejar ese caso en el código, los objetos se encargan.

2) Otro ejemplo de código que tiene mal olor es este que pongo a continuación y tiene que ver con la creación de una función no determinística de un atómata finito:

funcionNoDeterministica
| dictionary |
dictionary := Dictionary new.
dictionary
add: (Association key: #(1 'a') value: (Set new add:7;add:6 ));
add: (Association key: #(7 'a') value: (Set new add:3 ));
add: (Association key: #(3 'b') value: (Set new add:5 ));
add: (Association key: #(6 'b') value: (Set new add:2));
add: (Association key: #(6 'a') value: (Set new add:4));
add: (Association key: #(4 'b') value: (Set new add:2; add:5)).

^TransitionFunction dictionary: dictionary.

Si lo analizamos un poco, veremos que:
a) Nombre "dictionary" no agrega valor, no representa su rol en el contexto. En esta caso es difícil pensar un mejor nombre y creo que tiene que ver con el problema de usar un diccionario para representar las transiciones, pero esto es solo una suposición.
b) Se utiliza el mensaje #add: en vez de #at:put:
c) No queda claro qué representa la key ni el value. Por qué se usa como key un Array? y el value? Parecería por el código que se espera que el value sea un Set, pero qué termina siendo?... observen bien el código.... termina siendo un número puesto que el mensaje #add: devuelve el colaborador externo, no el receptor del mensaje. Esto demuestra además que los test realizado no son buenos, no cubren todos los casos.
d) No sería más claro tener algo así:

| transitions |

transitions := OrderedCollection new
add: (Transition from: 1 to: 7 by: $a);
add: (Transition from: 1 to: 6 by: $a);
add: (Transition from: 7 to: 3 by: $a);
....
yourself.

^TransitionFunction with: transitions

3 comentarios:

Anónimo dijo...

Hola Hernan,
Existe algun documento de diseno o algo similiar de Aconcagua.
No tengo experiencia en Smalltak pero me gustaria entender como lo hicieron.

Gracias y saludos.

Hernan Wilkinson dijo...

En el wiki de squeaksource, en la sección de FAQ, hay una buena descripción de Aconcagua. También el paquete está compuesto por una muy buena cantidad de tests que ayudan a entender su diseño y funcionamiento

Anónimo dijo...

http://markonzo.edu Hello good day ashley furniture [url=http://jguru.com/guru/viewbio.jsp?EID=1536072]ashley furniture[/url], 728, allegiant air [url=http://jguru.com/guru/viewbio.jsp?EID=1536075]allegiant air[/url], uyhftie, pressure washers [url=http://jguru.com/guru/viewbio.jsp?EID=1536078]pressure washers[/url], wexkjck, dishnetwork [url=http://jguru.com/guru/viewbio.jsp?EID=1536080]dishnetwork[/url], buctwm, adt security [url=http://jguru.com/guru/viewbio.jsp?EID=1536076]adt security[/url], tmrll,