Pregunta

Estoy escribiendo un estructurales de la herramienta de modelado para un civil enginering aplicación.Tengo un modelo de enorme clase que representa la totalidad del edificio, que incluyen colecciones de nodos, elementos de la línea, carga, etc.que también son clases personalizadas.

Ya he codificado una deshacer motor que guarda un profundo copia después de cada modificación en el modelo.Ahora empecé a pensar si podría haber codificado de manera diferente.En lugar de guardar el profundo copias, yo tal vez podría guardar una lista de cada modificador de acción con un correspondiente inversa modificador.De modo que yo pueda aplicar la inversa de modificadores para el actual modelo de deshacer, o la modificadores para rehacer.

Puedo imaginar cómo llevar a cabo comandos simples que cambiar las propiedades del objeto, etc.Pero ¿qué hay de comandos complejos?Como la inserción de un nuevo nodo de objetos para el modelo y la adición de algunos objetos de línea que mantener las referencias a los nodos nuevos.

Cómo se podría ir sobre la ejecución que?

¿Fue útil?

Solución

La mayoría de los ejemplos que he visto usan una variante de la Comando-Patrón para esto.Cada usuario de la acción que se puede deshacer obtiene su propia instancia de comando con toda la información para ejecutar la acción y rodar de nuevo.Usted puede mantener una lista de todos los comandos que se han ejecutado y se puede rodar de nuevo uno por uno.

Otros consejos

Creo que tanto recuerdo y el comando no son prácticos cuando se trata de un modelo del tamaño y el alcance que el OP implica.Que iba a funcionar, pero sería mucho trabajo para mantener y extender.

Para este tipo de problema, creo que usted necesita para construir en apoyo a su modelo de datos para apoyar el diferencial de puntos de control para el cada objeto involucrados en el modelo.Yo lo he hecho una vez y funcionó muy hábil.La cosa más grande que usted tiene que hacer es evitar la utilización directa de punteros o referencias en el modelo.

Cada referencia a otro objeto que se utiliza el identificador (como un número entero).Cuando el objeto es necesario, que la búsqueda de la definición actual del objeto a partir de una tabla.La tabla contiene una lista enlazada para cada objeto que contiene todas las versiones anteriores, junto con información respecto a que punto se activa para.

La aplicación de deshacer/rehacer es simple:Hacer su acción y establecer un nuevo punto de control;la reversión de todos los objetos de versiones para el punto de control anterior.

Se necesita un poco de disciplina en el código, pero tiene muchas ventajas:usted no necesita profundo de copias desde que usted está haciendo diferencial de almacenamiento del modelo de estado;usted puede ámbito de aplicación la cantidad de memoria que desea utilizar (muy importante para cosas como los modelos CAD) ya sea por número de rehacer o de la memoria utilizada;muy escalable y de bajo mantenimiento para las funciones que operan sobre el modelo, ya que no necesita hacer nada para implementar deshacer/rehacer.

Si usted está hablando de GoF, el Recuerdo patrón específicamente las direcciones de deshacer.

Como otros han dicho, el modelo de comando es un método muy eficaz de la aplicación de Deshacer/Rehacer.Pero hay una ventaja importante que me gustaría mencionar a la trama de comandos.

Cuando la aplicación de deshacer/rehacer con el modelo de comandos, usted puede evitar grandes cantidades de código duplicado mediante la abstracción (en cierto grado) las operaciones realizadas sobre los datos y utilizar las operaciones en la función de deshacer/rehacer el sistema.Por ejemplo, en un editor de texto cortar y pegar son complementarias de comandos (aparte de la gestión del portapapeles).En otras palabras, la operación de deshacer para un cortar pegar y la operación de deshacer para una pasta se corta.Esto se aplica tanto las operaciones más sencillas como escribir y borrar texto.

La clave aquí es que usted puede utilizar su función de deshacer/rehacer sistema como el principal sistema de comando para su editor.En lugar de escribir el sistema como "crear deshacer objeto, modificar el documento" puede "crear deshacer objeto, ejecutar rehacer la operación de deshacer objeto a modificar el documento".

Ahora, es cierto que, muchas personas están pensando a sí mismos "Bueno, duh, no es parte del punto de la trama de comandos?" Sí, pero he visto a muchos sistemas de comando que tiene dos conjuntos de comandos, uno para operaciones inmediatas y otro conjunto para deshacer/rehacer.No estoy diciendo que no habrá comandos que son específicos de las operaciones inmediatas y deshacer/rehacer, pero la reducción de la duplicación va a hacer el código más fácil de mantener.

Quizás usted quiera referirse a la Paint.NET código para sus deshacer de ellos tienen un muy buen sistema de deshacer.Probablemente sea un poco más sencillo de lo que usted necesita, pero puede darle algunas ideas y pautas.

-Adam

Este podría ser un caso donde CSLA es aplicable.Fue diseñado para proporcionar complejo de deshacer el apoyo a objetos en aplicaciones de Windows Forms.

He implementado un complejo de deshacer los sistemas correctamente utilizando el patrón Memento - muy fácil, y tiene la ventaja de que, naturalmente, proporcionando una Rehacer marco demasiado.Una forma más sutil de los beneficios es que el conjunto de acciones que pueden ser contenidas dentro de una sola Deshacer demasiado.

En pocas palabras, usted tiene dos pilas de objetos de recuerdo.Uno de Deshacer, y el otro para Rehacer.Cada operación crea un nuevo recuerdo, que, idealmente, serán algunas de las llamadas a cambiar el estado de su modelo, documento (o lo que sea).Esto se añade a la pila de deshacer.Cuando se hace una operación de deshacer, además de la ejecución de la acción de Deshacer en el Recuerdo objeto para cambiar el modelo de regreso, también pop el objeto fuera de la pila de Deshacer y empuje a la derecha en la puesta al día de la pila.

Cómo el método para cambiar el estado de su documento se implementa depende totalmente de su aplicación.Si usted puede simplemente hacer una llamada a la API (por ejemplo,ChangeColour(r,g,b)), entonces se puede anteponer una consulta para obtener y guardar el estado correspondiente.Pero el patrón también de soporte de decisiones profundo copias instantáneas de la memoria, los archivos temporales, la creación de etc - es todo depende de usted tal como es, es simplemente un método virtual de la aplicación.

Para hacer agregados de acciones (por ejemplo,el usuario de Turno -, se Selecciona una carga de objetos para hacer una operación, tales como eliminar, cambiar de nombre, cambio de atributo), el código crea una nueva pila de Deshacer como un solo recuerdo, y pasa a la operación real, para agregar cada uno de las operaciones.Así que sus métodos de acción no es necesario (a) global de la pila a preocuparse y (b) puede ser codificada de la misma si se ejecutan de forma aislada o como parte de un agregado de la operación.

Muchos de deshacer los sistemas están en memoria solamente, pero puede persistir la pila de deshacer si lo desea, supongo.

Acaba de leer acerca de la trama de comandos en mi desarrollo ágil libro - tal vez que tiene potencial?

Usted puede tener todos los comandos implementar la interfaz de comandos (la cual tiene un método Execute ()).Si desea deshacer, puede agregar una Deshacer método.

más info aquí

Estoy con Mendelt Siebenga en el hecho de que usted debe utilizar el Comando Patrón.El modelo utilizado fue el Recuerdo Patrón, que puede ser y será un gran desperdicio a lo largo del tiempo.

Puesto que usted está trabajando en un uso intensivo de la memoria de la aplicación, usted debe ser capaz de especificar la cantidad de memoria que deshacer el motor se puede tomar, el número de niveles de deshacer se guardan o algunas de almacenamiento a la que se conservan.Si no lo haces, pronto se enfrentará a los errores resultantes de la máquina que está fuera de la memoria.

Yo le aconsejo que revise si hay un marco de trabajo que ya ha creado un modelo para deshacer en el lenguaje de programación / marco de su elección.Es bueno para inventar cosas nuevas, pero es mejor tomar algo ya escrito, ajustado y probado en escenarios reales.Sería de gran ayuda si lo que estás escribiendo esto, para que la gente pueda recomendar marcos que saben.

Proyecto de Codeplex:

Es un marco simple para agregar Deshacer/Rehacer funcionalidad a sus aplicaciones, basado en el clásico Comando patrón de diseño.Es compatible con la fusión de las acciones, transacciones anidadas, retrasa la ejecución (ejecución en el nivel superior de confirmación de la transacción) y la posible no-lineal historial de deshacer (donde usted puede tener la opción de múltiples acciones para rehacer).

La mayoría de los ejemplos que he leído hacerlo mediante el comando o un recuerdo patrón.Pero se puede hacer sin patrones de diseño con un simple deque la estructura de.

Una forma inteligente de manejar deshacer, lo que haría que su software también es adecuado para múltiples colaboración de los usuarios, es la implementación de un transformación operativa de la estructura de datos.

Este concepto no es muy popular, pero bien definido y útil.Si la definición se ve demasiado abstracto para usted, este proyecto es un buen ejemplo de cómo una transformación operativa de objetos JSON es definido e implementado en Javascript

Para la referencia, he aquí una sencilla aplicación del modelo de Comandos de Deshacer/Rehacer en C#: Simple deshacer/rehacer sistema para C#.

Hemos reutilizado la carga del archivo y guardar código de serialización para "objetos" de una forma conveniente para guardar y restaurar el estado de un objeto.Hemos de empujar los objetos serializados en la pila de deshacer – junto con alguna información sobre lo que la operación se llevó a cabo y sugerencias en deshacer-ción de que la operación si no hay suficiente información reunida a partir de los datos serializados.Deshacer y Rehacer a menudo es sólo la sustitución de un objeto con otro objeto (en teoría).

Ha habido muchos muchos errores debido a los punteros (C++) a los objetos que nunca se fija como de realizar alguna extraña deshacer rehacer (secuencias de esos lugares no se actualiza más seguros para deshacer consciente "identificadores").Los errores en esta área a menudo ...ummm...interesante.

Algunas operaciones pueden ser casos especiales para la velocidad/el uso de los recursos - como el tamaño de las cosas, cosas que se mueven alrededor.

Multi-selección ofrece algunas interesantes complicaciones así.Por suerte, ya teníamos una agrupación concepto en el código.Kristopher Johnson comentario acerca de los sub-elementos es bastante cercano a lo que hacemos.

Tenía que hacer esto al escribir un solucionador para un peg-saltar juego de puzzle.He hecho cada movimiento de un objeto de Comando que se celebró la suficiente información que puede ser de hecho o deshecho.En mi caso esto era tan simple como el almacenamiento de la posición de partida y la dirección de cada movimiento.Yo, a continuación, almacena todos estos objetos en la pila, por lo que el programa puede deshacer fácilmente como muchos movimientos como sea necesario mientras retroceso.

Usted puede tratar de un ready-made de la implementación de Deshacer/Rehacer patrón en PostSharp. https://www.postsharp.net/model/undo-redo

Te permite añadir deshacer/rehacer funcionalidad a su aplicación sin necesidad de implementar el patrón de sí mismo.Se utiliza de grabación de patrón para el seguimiento de los cambios en el modelo y funciona con INotifyPropertyChanged patrón que también se implementa en PostSharp.

Se le proporciona controles de interfaz de usuario y usted puede decidir cuál es el nombre y la granularidad de cada operación será.

Una vez trabajé en una aplicación en la que todos los cambios realizados por un comando de la aplicación del modelo (es decir,CDocument...estábamos usando MFC) se conserva en el final de la línea, mediante la actualización de los campos en una base de datos interna se mantiene dentro del modelo.Lo que no se tiene que escribir por separado deshacer/rehacer código para cada acción.La pila de deshacer simplemente recordar las claves principales, los nombres de campo y los viejos valores cada vez que un registro se ha cambiado (al final de cada comando).

La primera sección de Patrones de Diseño (GoF, 1994) tiene un caso de uso para la implementación de la función de deshacer/rehacer como un patrón de diseño.

Usted puede hacer que su idea inicial de rendimiento.

Uso persistente de las estructuras de datos, y palo con el mantenimiento de un lista de referencias al antiguo estado alrededor de.(Pero que en realidad sólo funciona si las operaciones de todos los datos en su estado de clase son inmutables, y todas las operaciones de regresar de una nueva versión---pero la nueva versión no necesita ser una copia profunda, basta con sustituir las piezas cambiadas 'copy-on-write'.)

He encontrado el patrón Comando a ser muy útil aquí.En lugar de implementar varios inversa comandos, estoy usando rollback con el retraso en la ejecución de una segunda instancia de mi API.

Este enfoque parece razonable si se quiere reducir el esfuerzo de implementación y de fácil mantenimiento (y puede permitirse el extra de memoria para la 2ª instancia).

Vea aquí un ejemplo:https://github.com/thilo20/Undo/

No sé si esto va a ser de ninguna utilidad para usted, pero cuando tuve que hacer algo parecido en uno de mis proyectos, me terminó de descargar UndoEngine de http://www.undomadeeasy.com - un maravilloso motor y yo realmente no se preocupan demasiado acerca de lo que estaba bajo el capó - sólo funcionaba.

En mi opinión, la función de DESHACER/REHACER podría ser implementado en 2 formas ampliamente.1.Nivel de mando (llamado nivel de comando Deshacer/Rehacer) 2.Nivel de documento (llamado global de Deshacer/Rehacer)

Nivel de comando:Como muchas de las respuestas señalan, esto se realiza eficientemente el uso de Recuerdo patrón.Si el comando también apoya el diario de la acción, una puesta al día es fácilmente compatible.

Limitación:Una vez que el ámbito de aplicación de la orden es, el de deshacer/rehacer es imposible, lo que conduce a nivel de documento(global) de deshacer/rehacer

Supongo que tu caso encajaría en el mundial de deshacer/rehacer, ya que es apropiado para un modelo que implica una gran cantidad de espacio de memoria.También, este es adecuado para selectivamente deshacer/rehacer también.Hay dos tipos primitivos

  1. Toda la memoria de deshacer/rehacer
  2. Nivel de objeto Deshacer Rehacer

En "memoria de Todos Deshacer/Rehacer", toda la memoria que se trata de datos conectado (como un árbol, o una lista o un gráfico) y la memoria es gestionada por la aplicación, en lugar de la OS.Así que de nuevo y eliminar a los operadores si en C++ están sobrecargados para contener más estructuras específicas para implementar de manera efectiva las operaciones, tales como.Si cualquier nodo es modificado, b.la celebración y borrado de datos, etc., La manera en que funciona es básicamente copiar toda la memoria(suponiendo que la asignación de memoria ya está optimizada y gestionados por la aplicación que utiliza algoritmos avanzados) y guárdelo en una pila.Si la copia de la memoria se solicita, la estructura de árbol se copia basa en la necesidad de tener una profundidad de copia.Una copia profunda sólo se realiza para que la variable que se modifica.Dado que cada variable se asigna el uso personalizado de la asignación, la aplicación tiene la última palabra cuando a eliminar si es necesario.Las cosas se vuelven muy interesante si tenemos a la partición de la función de Deshacer/Rehacer cuando lo que ocurre es que necesitamos a través de programación-selectivamente Deshacer/Rehacer un conjunto de la operación.En este caso, sólo aquellas variables nuevas o eliminar variables o variables modificadas se les da una bandera para que Deshacer/Rehacer solo deshace/rehace aquellos memoria Las cosas se vuelven aún más interesante si tenemos que hacer un parcial de Deshacer/Rehacer dentro de un objeto.Cuando tal es el caso, una nueva idea de "Visitante patrón" se utiliza.Se llama "Nivel de Objeto de Deshacer/rehacer"

  1. Nivel de objeto de Deshacer/Rehacer:Cuando la notificación para deshacer/rehacer se llama, cada objeto implementa un flujo de operación en donde, la serpentina, obtiene el objeto con los datos antiguos/nuevos datos que está programado.Los datos que es de no ser molestado se deja reposar.Cada objeto es un streamer como argumento y dentro de la función de Deshacer/Rehacer llamada, corrientes/unstreams los datos del objeto.

1 y 2 podría tener la aplicación de métodos tales como 1.BeforeUndo() 2.AfterUndo() 3.BeforeRedo() 4.AfterRedo().Estos métodos han de ser publicado en el básico de Deshacer/rehacer Comando ( no el contexto de comandos), de modo que todos los objetos de la implementación de estos métodos para obtener una acción específica.

Una buena estrategia es crear un híbrido de 1 y 2.La belleza es que estos métodos(1&2) de sí mismos el uso de comandos de patrones

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top