Revisamos exhaustivamente cada uno de los artículos en nuestro sitio web con la meta de mostrarte en todo momento la información más veraz y actualizada.
Solución:
Hay dos patrones clásicos para usar. El primero es el patrón de recuerdo que se utiliza para almacenar instantáneas del estado completo de su objeto. Esto es quizás más intensivo en el sistema que el patrón de comando, pero permite retroceder de manera muy simple a una instantánea anterior. Puede almacenar las instantáneas en disco como PaintShop / PhotoShop o guardarlas en la memoria para objetos más pequeños que no requieren persistencia. Lo que está haciendo es exactamente para lo que se diseñó este patrón, por lo que debería ajustarse un poco mejor que el Patrón de comando sugerido por otros.
Además, una nota adicional es que debido a que no requiere que tenga comandos recíprocos para deshacer algo que se hizo anteriormente, significa que cualquier función potencialmente unidireccional [such as hashing or encryption] que no se puede deshacer trivialmente usando comandos recíprocos, aún se puede deshacer de manera muy simple simplemente retrocediendo a una instantánea anterior.
También como se señaló, el patrón de comando es potencialmente menos intensivo en recursos, por lo que concederé que en casos específicos donde:
- Hay un estado de objeto grande que debe persistir y / o
- No hay métodos destructivos y
- Donde los comandos recíprocos se pueden usar de manera muy trivial para revertir cualquier acción tomada
el patrón de comando mayo encajar mejor [but not necessarily, it will depend very much on the situation]. En otros casos, usaría el patrón de recuerdo.
Probablemente me abstendría de usar una combinación de los dos porque tiendo a preocuparme por el desarrollador que vendrá detrás de mí y mantendrá mi código, además de que es mi responsabilidad ética con mi empleador hacer que ese proceso sea tan simple y económico como posible. Veo que una combinación de los dos patrones se convierte fácilmente en un agujero de rata inmanejable de incomodidad que sería costoso de mantener.
Existen tres enfoques aquí que son viables. Patrón de recuerdo (instantáneas), patrón de comando y diferenciación de estado. Todos tienen ventajas y desventajas y realmente se reduce a su caso de uso, con qué datos está trabajando y qué está dispuesto a implementar.
Optaría por State Diffing si puede salirse con la suya, ya que combina la reducción de memoria con facilidad de implementación y mantenibilidad.
Voy a citar un artículo que describe los tres enfoques (referencia a continuación).
Tenga en cuenta que VoxelShop mencionado en el artículo es de código abierto. Entonces puede echar un vistazo a la complejidad del patrón de comando aquí: https://github.com/simlu/voxelshop/tree/develop/src/main/java/com/vitco/app/core/data/history
A continuación se muestra un extracto adaptado del artículo. Sin embargo, le recomiendo que lo lea en su totalidad.
Patrón de recuerdo
Cada estado histórico almacena una copia completa. Una acción crea un nuevo estado y se usa un puntero para moverse entre los estados y permitir deshacer y rehacer.
Pros
- La implementación es independiente de la acción aplicada. Una vez implementado podemos agregar acciones sin preocuparnos por romper el historial.
- Es rápido avanzar a una posición predefinida en la historia. Esto es interesante cuando las acciones aplicadas entre la posición histórica actual y la deseada son computacionalmente costosas.
Contras
- Los requisitos de memoria pueden ser significativamente más altos en comparación con otros enfoques.
- El tiempo de carga puede ser lento si las instantáneas son grandes.
Patrón de comando
Similar al patrón Memento, pero en lugar de almacenar el estado completo, solo se almacena la diferencia entre los estados. La diferencia se almacena como acciones que se pueden aplicar y no aplicar. Al introducir una nueva acción, es necesario implementar la aplicación y la anulación de la aplicación.
Pros
- La huella de memoria es pequeña. Solo necesitamos almacenar los cambios en el modelo y, si son pequeños, la pila del historial también es pequeña.
Contras
- No podemos ir directamente a una posición arbitraria, sino que debemos anular la aplicación de la pila de historial hasta que lleguemos allí. Esto puede llevar mucho tiempo.
- Cada acción y su reverso deben encapsularse en un objeto. Si su acción no es trivial, esto puede resultar difícil. Los errores en la acción (inversa) son realmente difíciles de depurar y pueden resultar fácilmente en bloqueos fatales. Incluso las acciones de apariencia simple suelen implicar una gran cantidad de complejidad. Por ejemplo, en el caso del Editor 3D, el objeto para agregar al modelo debe almacenar lo que se agregó, qué color se seleccionó actualmente, qué se sobrescribió, si el modo espejo está activo, etc.
- Puede ser difícil de implementar y requiere mucha memoria cuando las acciones no tienen un reverso simple, por ejemplo, al desenfocar una imagen.
Diferencia de estado
Similar al patrón de comando, pero la diferencia se almacena independientemente de la acción simplemente xor-nig los estados. La introducción de una nueva acción no requiere ninguna consideración especial.
Pros
- La implementación es independiente de la acción aplicada. Una vez que se agrega la funcionalidad de historial, podemos agregar acciones sin preocuparnos por romper el historial.
- Los requisitos de memoria suelen ser mucho más bajos que para el enfoque de instantánea y, en muchos casos, son comparables al enfoque de patrón de comando. Sin embargo, esto depende en gran medida del tipo de acciones aplicadas. Por ejemplo, invertir el color de una imagen usando el patrón de comando debería ser muy económico, mientras que la diferencia de estado guardaría toda la imagen. A la inversa, cuando se dibuja una línea larga de forma libre, el enfoque del Patrón de comando podría usar más memoria si encadenaba las entradas del historial para cada píxel.
Contras / Limitaciones
- No podemos ir directamente a una posición arbitraria, sino que debemos anular la aplicación de la pila de historial hasta que lleguemos allí.
- Necesitamos calcular la diferencia entre estados. Esto puede resultar caro.
- La implementación de xor diff entre los estados del modelo puede ser difícil de implementar según su modelo de datos.
Referencia:
https://www.linkedin.com/pulse/solving-history-hard-problem-lukas-siemon
La práctica clásica es seguir el patrón de comando.
Puede encapsular cualquier objeto que realice una acción con un comando y hacer que realice la acción inversa con un método Undo (). Almacena todas las acciones en una pila para poder rebobinarlas fácilmente.