usuario tecnicas que para experiencia experience estrategias ejemplos diseño desarrollo user-interface finite-automata

user interface - tecnicas - Máquinas estatales e interfaz de usuario: ¿algún ejemplo/experiencia?



ui para web (10)

Acabamos de hablar sobre la construcción de la interfaz de usuario de Horrocks con Statecharts , precios de segunda mano desde $ 250 hasta casi $ 700. Nuestro gerente de desarrollo de software lo califica como uno de los libros más importantes que tiene (lamentablemente, vive en el otro lado del mundo).

Los libros de Samek en los cuadros de estado se basan significativamente en este trabajo, aunque en un dominio ligeramente diferente y, según los informes, no tan claros. Los " Diagramas de estado UML prácticos en programación controlada por eventos C / C ++ para sistemas integrados " también están disponibles en Safari .

Horrocks se cita bastante: hay veinte documentos en el portal de ACM, así que si tiene acceso allí, puede encontrar algo útil.

Hay un libro y software FlashMX para simulación interactiva . Tienen un capítulo de muestra en PDF sobre gráficos de estado.

Objetos, componentes y marcos con UML: El enfoque de Catalysis (SM) tiene un capítulo sobre Modelos de comportamiento que incluye aproximadamente diez páginas de ejemplos útiles sobre el uso de diagramas de estados (tengo en cuenta que está disponible a bajo costo de segunda mano). Es bastante formal y pesado pero esa sección es fácil de leer.

Estoy buscando formas de desvincular mi código de widget de front-end. Se ha sugerido que una Máquina de estados finitos es la forma correcta de pensar sobre lo que estoy haciendo. Sé que un paradigma de máquina de estado se puede aplicar a casi cualquier problema. Me pregunto si hay algunos programadores de IU experimentados que realmente se acostumbren a esto.

Entonces, la pregunta es: ¿alguno de ustedes programadores de UI piensa en términos de Máquinas estatales en su trabajo? ¿Si es así, cómo?

gracias, -Morgan


Actualmente estoy trabajando con un marco (propietario) que se presta bien al paradigma de la IU como máquina de estado, y definitivamente puede reducir (pero no eliminar) los problemas con interacciones complejas e imprevistas entre los elementos de la IU.

El principal beneficio es que le permite pensar en un nivel más alto de abstracción, en una mayor granularidad. En lugar de pensar "Si se presiona el botón A, entonces el cuadro combinado B se bloquea, el campo de texto C se borra y el botón D se desbloquea", usted piensa "Al presionar el botón A pone la aplicación en el estado COMPROBADO", y entrar en ese estado significa que ciertas cosas ocurrir.

No creo que sea útil (o incluso posible) modelar toda la interfaz de usuario como una máquina de estado único. En cambio, generalmente hay varias máquinas de estado más pequeñas que manejan una parte de la IU (que consta de varios controles que interactúan y pertenecen conceptualmente), y una (quizás más de una) máquina de estado "global" que maneja problemas más fundamentales.


Cada elemento de interfaz que se presenta al usuario puede pasar a otro estado del actual. Básicamente, necesitas crear un mapa de qué botón puede conducir a qué otro estado.

Esta asignación le permitirá ver los estados no utilizados o aquellos en los que múltiples botones o rutas pueden conducir al mismo estado y no a otros (que pueden combinarse).


Hay un libro sobre este tema. Lamentablemente está agotado y los raros usados ​​disponibles son muy caros.

Constructing the User Interface with Statecharts by Ian Horrocks, Addison-Wesley, 1998


Hola, Morgan, estamos construyendo un marco de trabajo personalizado en AS3 aquí en Radical y usamos el paradigma de máquina de estado para impulsar cualquier actividad de IU de interfaz.

Tenemos una configuración de máquina de estado para todos los eventos de botón, todos los eventos de visualización y más.

AS3, al ser un lenguaje impulsado por eventos, hace de esto una opción muy atractiva.

Cuando se detectan ciertos eventos, los estados de los botones / objetos de visualización se cambian automáticamente.

¡Tener un conjunto general de estados definitivamente podría ayudar a desvincular tu código!


No es la IU la que debe modelarse como una máquina de estado; son los objetos que se muestran que puede ser útil modelar como máquinas de estado. Su UI luego se convierte (simplificación excesiva) en un grupo de manejadores de eventos para cambio de estado en varios objetos.

Es un cambio de:

DoSomethingToTheFooObject(); UpdateDisplay1(); // which is the main display for the Foo object UpdateDisplay2(); // which has a label showing the Foo''s width, // which may have changed ...

a:

Foo.DoSomething(); void OnFooWidthChanged() { UpdateDisplay2(); } void OnFooPaletteChanged() { UpdateDisplay1(); }

Pensando en qué cambios en los datos que está mostrando deben causar que el repintado pueda ser esclarecedor, tanto desde el lado de la interfaz de usuario del cliente como desde el lado de Foo del servidor.

Si encuentra que, de los 100 UI que necesitarán ser repintados cuando el estado de Foo cambia, todos ellos tienen que volverse a dibujar cuando la paleta cambia, pero solo 10 cuando el ancho cambia, puede sugerir algo acerca de qué eventos / estado cambios Foo debería estar señalando. Si encuentra que tiene un controlador de eventos grande OnFooStateChanged () que revisa varias propiedades de Foo para ver qué ha cambiado, en un intento de minimizar las actualizaciones de UI, sugiere algo sobre la granularidad del modelo de eventos de Foo. Si desea escribir un pequeño widget de interfaz de usuario independiente, puede usarlo en varios lugares de la interfaz de usuario, pero necesita saber cuándo cambia Foo y no desea incluir todo el código que trae la implementación de Foo. sugiere algo sobre la organización de sus datos en relación con su UI, donde usa clases vs interfaces, etc. Fundamentalmente, le hace pensar más seriamente sobre cuál es su capa de presentación, más en serio que "todo el código en mi formar clases ".

-ORDENADOR PERSONAL


No es realmente un problema de UI, para ser honesto.

Haría lo siguiente:

  1. Defina sus estados
  2. Defina sus transiciones, ¿qué estados son accesibles y cuáles otros?
  3. ¿Cómo se desencadenan estas transiciones? ¿Cuáles son los eventos?
  4. Escriba su máquina de estado: almacene el estado actual, reciba eventos y, si ese evento puede causar una transición válida desde el estado actual, cambie el estado según corresponda.

Obtuve una presentación previa sobre un patrón que llamo "Estado primero".

Es una combinación de MPV / IoC / FSM y lo he usado con éxito en .Net / WinForms, .Net / Silverlight y Flex (en este momento).

Empiezas codificando tu FSM:

class FSM IViewFactory ViewFactory; IModelFactory ModelFactory; Container Container; // e.g. a StackPanel in SL ctor((viewFactory,modelFactory,container) { ...assignments... start(); } start() { var view = ViewFactory.Start(); var model = ModelFactory.Start(); view.Context = model; view.Login += (s,e) => { var loginResult = model.TryLogin(); // vm contains username/password now if(loginResult.Error) { // show error? } else { loggedIn(loginResult.UserModel); // jump to loggedIn-state } }; show(view); } loggedIn(UserModel model) { var view = ViewFactory.LoggedIn(); view.Context = model; view.Logout += (s,e) => { start(); // jump to start }; show(view); }

A continuación, crea su IViewFactory e IModelFactory (su FSM le facilita ver lo que necesita)

public interface IViewFactory { IStartView Start(); ILoggedInView LoggedIn(); } public interface IModelFactory { IStartModel Start(); }

Ahora todo lo que necesita hacer es implementar IViewFactory , IModelFactory , IStartView , ILoggedInView y los modelos. La ventaja aquí es que puede ver todas las transiciones en el FSM, obtiene un acoplamiento über-low entre las vistas / modelos, alta capacidad de prueba y (si su lenguaje lo permite) una gran cantidad de tipos de forma segura.

Un punto importante en el uso de la FSM es que no solo debe saltar entre los estados; también debe llevar todos los datos de estado con usted en el salto (como argumentos, consulte loggedIn arriba). Esto lo ayudará a evitar estados globales que generalmente arrojan gui-código.

Puede ver la presentación en http://prezi.com/bqcr5nhcdhqu/ pero no contiene ejemplos de código en este momento.


Una máquina de estado es algo que permite que el código funcione con otras máquinas de estado. Una máquina de estados es simplemente lógica que tiene memoria de eventos pasados.

Por lo tanto, los humanos son máquinas de estado, y a menudo esperan que su software recuerde lo que han hecho en el pasado para que puedan continuar.

Por ejemplo, puede colocar toda la encuesta en una página, pero las personas se sienten más cómodas con páginas de preguntas más pequeñas. Lo mismo con los registros de usuarios.

Así que la máquina de estados tiene mucha aplicabilidad a las interfaces de usuario.

Sin embargo, deben ser entendidos antes de ser implementados, y el diseño completo debe estar completo antes de que se escriba el código: la máquina de estado puede, es y será abusada, y si no tiene una idea muy clara de por qué está usando uno, y cuál es el objetivo, puede terminar peor que otras técnicas.

-Adán


Las máquinas de estado generalmente son de muy bajo nivel para ayudarlo a pensar en una interfaz de usuario. Hacen una buena elección de implementación para un kit de herramientas de interfaz de usuario, pero hay demasiados estados y transiciones para describir en una aplicación normal para que los describa a mano.

Me gusta pensar en UI con continuaciones. (Google it - el término es lo suficientemente específico como para obtener muchos éxitos de alta calidad).

En lugar de que mis aplicaciones estén en varios estados representados por indicadores de estado y modos, utilizo las continuas para controlar lo que hace la aplicación a continuación. Es más fácil de explicar con un ejemplo. Supongamos que desea abrir un cuadro de diálogo de confirmación antes de enviar un correo electrónico. El paso 1 crea un correo electrónico. El paso 2 obtiene la confirmación. El paso 3 envía el correo electrónico. La mayoría de los juegos de herramientas de UI requieren que transfiera el control nuevamente a un bucle de evento después de cada paso, lo que hace que esto sea realmente feo si intenta representarlo con una máquina de estado. Con las continuaciones, no piensa en términos de los pasos que el conjunto de herramientas le impone; es todo un proceso de creación y envío de un correo electrónico. Sin embargo, cuando el proceso necesita la confirmación, usted captura el estado de su aplicación en una continuación y entrega esa continuación al botón Aceptar en el cuadro de diálogo de confirmación. Cuando se presiona OK, su aplicación continúa desde donde estaba.

Las continuas son relativamente raras en los lenguajes de programación, pero afortunadamente puede obtener una versión de un hombre pobre utilizando cierres. Volviendo al ejemplo de envío de correo electrónico, en el momento en que necesita obtener la confirmación, escribe el resto del proceso como cierre y luego entrega el cierre al botón Aceptar. Los cierres son algo así como las subrutinas anónimas anónimas que recuerdan los valores de todas sus variables locales la próxima vez que se llaman.

Esperemos que esto te brinde algunas nuevas direcciones en las que pensar. Trataré de volver más tarde con un código real para mostrarte cómo funciona.

Actualización: Aquí hay un ejemplo completo con Qt en Ruby. Las partes interesantes están en ConfirmationButton y MailButton. No soy un experto en Qt o Ruby, por lo que agradecería cualquier mejora que puedan ofrecer.

require ''Qt4'' class ConfirmationWindow < Qt::Widget def initialize(question, to_do_next) super() label = Qt::Label.new(question) ok = ConfirmationButton.new("OK") ok.to_do_next = to_do_next cancel = Qt::PushButton.new("Cancel") Qt::Object::connect(ok, SIGNAL(''clicked()''), ok, SLOT(''confirmAction()'')) Qt::Object::connect(ok, SIGNAL(''clicked()''), self, SLOT(''close()'')) Qt::Object::connect(cancel, SIGNAL(''clicked()''), self, SLOT(''close()'')) box = Qt::HBoxLayout.new() box.addWidget(label) box.addWidget(ok) box.addWidget(cancel) setLayout(box) end end class ConfirmationButton < Qt::PushButton slots ''confirmAction()'' attr_accessor :to_do_next def confirmAction() @to_do_next.call() end end class MailButton < Qt::PushButton slots ''sendMail()'' def sendMail() lucky = rand().to_s() message = "hello world. here''s your lucky number: " + lucky do_next = lambda { # Everything in this block will be delayed until the # the confirmation button is clicked. All the local # variables calculated earlier in this method will retain # their values. print "sending mail: " + message + "/n" } popup = ConfirmationWindow.new("Really send " + lucky + "?", do_next) popup.show() end end app = Qt::Application.new(ARGV) window = Qt::Widget.new() send_mail = MailButton.new("Send Mail") quit = Qt::PushButton.new("Quit") Qt::Object::connect(send_mail, SIGNAL(''clicked()''), send_mail, SLOT(''sendMail()'')) Qt::Object::connect(quit, SIGNAL(''clicked()''), app, SLOT(''quit()'')) box = Qt::VBoxLayout.new(window) box.addWidget(send_mail) box.addWidget(quit) window.setLayout(box) window.show() app.exec()