Hola gente,
Estuve algunos días ocupado con otras cosas, y el SWT/ST2JS/etc no
avanzaron mucho. Ahora estoy por volver a la actividad, y como el
movimiento se demuestra andando les comento en que estado estamos.
Sigo detenido en el punto MVC-Distribuido. Si bajan las últimas
versiones, verán que está funcionando relativamente bien.
La versión actual prueba (creo, decidan ustedes mismos) que es posible
hacer una aplicación MVC donde el M(odel) esté en el server (Smalltalk)
y el V(iew)/C(ontroller) esté en el cliente (Browser de Internet). Para
lograr tiempos de respuesta adecuados, algunas optimizaciones ya fueron
hechas y funcionan bastante bien.
Para acelerar los tiempos de respuesta, básicamente hay que reducir la
cantidad de requests desde los clientes.
Mensajes Asincrónicos/Cola de mensajes asincrónicos:
----------------------------------------------------
En una aplicación típica MVC, el View se conecta a MUCHOS eventos del
modelo. Cada suscripción implica un envío de mensaje. Si el Modelo
está remoto, eso implica 1 request por internet. Imaginemos un simple
objeto de clase Cliente, con algunos aspectos como #nombre, #apellido,
#fechaDeNacimiento y #dni. Para crear una Vista con esos aspectos, el
View/Controller deberá suscribirse a los eventos de nombre #nombre,
#apellido, #fechaDeNacimiento y #dni... es decir: 4 requests.
Si a eso le sumamos que, antes de suscribirse hay que hacer un request
para pedir una referencia remota, tendremos que hacer N+1 requests para
crear una vista sobre un objeto con N aspectos.
El caso empeora cuando cargamos un objeto en la vista y descargamos
otros. En ese caso necesitamos N+1 requests para el objeto nuevo
(siendo N la cantidad de aspectos del objeto nuevo) y M requests para
desuscribirse de los eventos del objeto viejo (siendo M la cantidad de
aspectos del objeto viejo).
Por supuesto eso mataría la velocidad del cliente.
Sin embargo encontré una forma de optimización que funciona muy bien, y
logra resolver el caso dado (con o sin objeto viejo) haciendo SIEMPRE 2
requests: 1 para obtener la referencia remota al objeto nuevo, y 1 para
hacer (en batch) TODAS las desuscripciones y suscripciones.
El truco consiste en lo siguiente:
- Cuando hacemos un envío de mensaje al #serverSide, y no
necesitamos un valor de respuesta, podemos hacer el envío
asincrónico. Para poder tomar ventaja de esto, el proxy
generado del ServerApplication incluye 2 métodos por cada método
del ServerApplication, uno para invocarlo sincrónicamente y otro
para hacer asíncrono.
- El framework permite encolar muchos envíos de mensajes
asincrónicos en 1 sólo pedido, simplemente haciendo algo como:
aRemoteModel. "Referencia remote obtenid"
self isolatedAsynchronousRPCMethods: [
mainPanel clearWidgets. "Limpia los widgets viejos, causando que el browser se desconecte de los eventos al modelo anterior."
mainPanel addWidget: aRemoteModel defaultView. "Crea la nueva vista al nuevo modelo, causando la conexión a los eventos del modelo nuevo."
].
(Ver senders/implementors de #isolatedAsynchronousRPCMethods:)
Eventos extendidos:
-------------------
Otro patrón típico de las aplicaciones MVC es que el View envíe un
mensaje (por ejemplo el mensaje #nombre) cuando se entera que el modelo
cambió (por estar suscripto al evento de nombre #nombre). Si tenemos en
cuenta que el Modelo está en el servidor, y que las Vistas/Controladores
están en los browsers, veremos que cuando el modelo cambia (y el evento
se propaga a los browsers, usando la conexión Comet) todas las vistas
enviarán un mensaje al Server para obtener el nuevo valor del modelo.
Para optimizar ese caso, expandí (levemente) el mecanismo de eventos.
Así se suscribe un Aspecto (SWTAspect) al modelo.
self model
when: self eventName
send: #modelChanged:
to: self
withResultsOfSelectors: {self getter}
La clave está en la última parte del mensaje: "withResultsOfSelectors:
{self getter}". Cuando ocurre un evento de nombre "self eventName", el
modelo enviará el mensaje "#modelChanged:" a "self", INCLUYENDO en el
envío el resultado de enviar el mensaje "self getter" al modelo.
Con ese "truco", el evento se propaga con la información extra necesaria
evitando que los clientes (browsers) tengan que hacer un pedido.
¿Cómo sigue el trabajo? --> Hay que lograr que un usuario del framework
no tenga que manejar a mano la complejidad inherente a tener un MVC
distribuido. Para eso el framework debe auto-optimizarse (usando estos
trucos, y otros que se nos ocurran) desde el uso normal. Este objetivo
está, en parte, también resuelto: Si uno se limita a usar los Aspects
entregados por SWTModel para crear las vistas, tendremos todas estar
optimizaciones ya hechas.
Entonces ahora toca encontrar una forma de describir los modelos, que
sea Smalltalk-way, pero que tenga el nivel de abstracción correcto para
poder meter optimizaciones por debajo. En la versión actual la
sobre-cargada-de-responsabilidades clase SWTAspect, y la clase
SWTRemoteModel logran esconder la complejidad... pero, como les dije,
SWTAspect es casi un engendro de mezclas de responsabilidades.
Después de esta sábana de email, les dejo estas preguntas:
- ¿Qué framework de hacer UIs (de Smalltalk u otro lenguaje) les
parece más poderoso?
- ¿Qué tipo de framework permitiría crear vistas automáticas?
-- ¿Cuanto de difícil es tunear a mano esas vistas automáticas?
- ¿Vieron el NakedObjects (http://nakedobjects.org/)? ¿Qué les
parece?
Saludos,
-- Diego
--
==========================================
Diego Gomez Deck
------------------------------------------
http://diegogomezdeck.blogspot.com/
http://smalltalk.consultar.com/
==========================================