martes, 14 de abril de 2009

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.


1 comentario:

Pablo dijo...

Hernan, veo que tus comentarios han tenido efecto sobre Magritte -> el método autoAccessor: no esta mas.

Que opinas de la idea de "Describir una vez, tenerlo en todos lados?"

"Describe once,
Get everywhere"