2011-11-23 10 views
6

Así que estoy implementando una GUI svg-editor-like en una aplicación que estoy trabajando aquí. Estos son algunos ejemplos de la lógica que sería necesaria en él:Consejos arquitectónicos sobre la implementación de la lógica de GUI

  • Si el usuario hace clic con el botón derecho sobre el lienzo, un nuevo nodo debe ser creado, y de los siguientes debe ser "unido" con una línea , formando un polígono
  • Si el usuario hace clic con el botón izquierdo en un nodo, que se debe mover todo el conjunto de polígonos de acuerdo a la posición del ratón
  • el usuario es capaz de eliminar nodos
  • nodos seleccionados debe ser de color de manera diferente
  • El usuario puede seleccionar varios nodos mediante pressi ng MAYÚS y hacer clic en los nodos

Y así sucesivamente.

que ya haya implementado todos estos elementos, pero no me gustó el resultado final, sobre todo porque tenía que utilizar una gran cantidad de banderas para manipular los estados (ratón hace clic en el botón izquierdo & & & & no se mueve? Hacer esto), y seguramente este código podría ser más elegante. Así que he investigado un poco y llegué a estas opciones:

  • patrón de Pipeline: me gustaría crear clases que se ocuparía de cada evento lógico separado, y utilizar un orden de prioridad para proporcionar qué hacer/lo haría ser entregados primero, y cómo el evento se propagaría a los artículos posteriores de Pipeline.

  • MVC: esta es la respuesta más común, pero la forma en que podría usarla para hacer que el código esté más limpio es muy borrosa para mí en este momento.

  • Estado de la máquina: Eso estaría bien, pero la gestión de la granularidad de la máquina de estados sería complicado

Así que estoy pidiendo al S. O. gurús en consejos sobre cómo construir un código mejor y más feliz.

Respuesta

6

Sugiero separar la lógica para el mapeo de las entradas de la interfaz de usuario a una operación específica en objetos dedicados. Vamos a llamarlos objeto Sensor. Sin conocer su lenguaje de implementación, voy a ser genérico con esto, pero debe entenderse.

OperationSensor 
+ OnKeyDown 
+ OnKeyPress 
+ OnKeyUp 
+ OnLeftMouseDown 
+ OnLeftMouseUp 
+ OnNodeSelect 
+ OnNodeDeselect 
+ OnDragStart 
+ OnDragStop 

Digamos que usted tiene una clase central que los agregados de todas las diversas entradas de interfaz de usuario, UiInputManager. Utiliza los mecanismos específicos del idioma para escuchar la entrada del teclado y el mouse. También detecta operaciones básicas como detectar que si se presiona el mouse y luego se mueve, eso es un "arrastre" lógico.

UiInputManager 
// event listeners 
+ keyboard_keydownHandler 
+ keyboard_keyupHandler 
+ mouse_leftdownHandler 
+ mouse_rightdownHandler 
// active sensor list, can be added to or removed from 
+ Sensors 

El UiInputManager NO es responsable de saber qué operaciones están causando esas entradas. Simplemente notifica a sus sensores de una manera específica del idioma.

foreach sensor in Sensors 
    sensor.OnDragStarted 

o, si los sensores detectar eventos lógicos emitidos por el UiInputManager

RaiseEvent DragStarted 

Lo que tenemos ahora es la tubería de entrada a la ruta a las subclases OperationSensor. Cada OperationSensor tiene la lógica que pertenece a una sola operación. Si detecta que se han cumplido los criterios de la operación, entonces crea el objeto de comando apropiado y lo vuelve a subir.

// Ctrl + zooms in, Ctrl - zooms out 
ZoomSensor : OperationSensor 

    override OnKeyDown 
    { 
     if keyDown.Char = '+' && keyDown.IsCtrlDepressed 
     base.IssueCommand(new ZoomCommand(changeZoomBy:=10) 
     elseif keyDown.Char = '-' && keyDown.IsCtrlDepressed 
     base.IssueCommand(new ZoomCommand(changeZoomBy:=-10)         
    } 

Recomendaría que los objetos de comando pasen de los sensores al UiInputManager. El administrador puede pasarlos a su subsistema de procesamiento de comandos. Esto le da al administrador la oportunidad de tal vez notificar a los sensores que una operación se completó, lo que les permite restablecer su estado interno si es necesario.

Las operaciones de varios pasos pueden manejarse de dos maneras diferentes. Puede implementar máquinas de estado interno dentro de un SensorOperation, o puede hacer que un sensor "paso 1" cree un sensor "paso 2" y agregarlo a la lista de sensores activos, posiblemente incluso eliminándose de la lista. Cuando se completa el "paso 2", puede volver a agregar el sensor "paso 1" y eliminarse.

+0

Entonces, ¿está diciendo que debería usar un gasoducto como dijo en su pregunta? – jgauffin

+1

Estaba sugiriendo algo nuevo. (Creo) dijo que deseaba usar una tubería para capturar y canalizar los eventos, que luego se encadena a los manejadores de sucesos posteriores hasta que el manejador final se ocupa del evento. Me concentré en las operaciones que se realizan en lugar de los eventos que las desencadenan. Sin embargo, si el OP aclarara su idea principal, podría comparar/contrastar un poco más. Espero que OP responda para ver su opinión sobre esto. – tcarvin

+0

+1 para la sugerencia y la aclaración – jgauffin

2

Martin Fowler tiene a good writeup en MVC y patrones relacionados. También es posible que desee consultar el command pattern para dejar que los elementos de la IU descubran cómo deben comportarse (es decir, cuando un clic en un nodo debe moverlo o eliminarlo, etc.)

+0

Sí, ya estoy usando el patrón de comando para implementar la ejecución de la acción. Estoy más centrado en arreglar el lío lógico ahora mismo. – scooterman

0

Para IU MVC es bastante universal en estos días . Muy brevemente, el M (modelo) contiene estado, V (vista) muestra elementos visuales, C (controlador) distribuye las acciones del usuario entrante, como los clics del mouse. El objetivo es que el modelo no se preocupe directamente por la vista, excepto tal vez por eventos de disparo.

Quizás pondría la 'inteligencia' en los modelos. El modelo sabría cuándo se selecciona un nodo, sus nodos adyacentes forman el polígono, las máquinas de estado, etc. Este diseño le brinda varios beneficios. Es independiente de los detalles de la representación de la interfaz de usuario; por lo que puede realizar cambios importantes en la vista sin interrumpir la funcionalidad principal. También hace mucho más fácil la prueba de la unidad.

Cuestiones relacionadas