Pregunta

necesito para implementar marco de trabajo de deshacer / rehacer para mi aplicación ventana (editor como PowerPoint), lo que debería ser la mejor práctica a seguir, cómo sería manejar todos los cambios en las propiedades de los objetos de mi y su reflexión sobre el interfaz de usuario.

¿Fue útil?

Solución

Hay dos patrones clásicos para su uso. El primero es el href="http://www.dofactory.com/Patterns/PatternMemento.aspx" rel="noreferrer"> recuerdo patrón

Además, una nota adicional es que, ya que no requiere que usted tenga comandos recíprocas para deshacer algo que se hizo anteriormente, significa que cualquier potencialmente función unidireccional [como hash o cifrado] que no se pueden deshacer trivialmente usando comandos recíprocos todavía se puede deshacer de manera muy sencilla con sólo hacer retroceder a una instantánea más.

También como se señaló, el patrón de comando que es potencialmente menos recursos, por lo voy a admitir que en casos específicos en:

  • Hay un estado objeto grande que se persistió y / o
  • No existen métodos destructivos y
  • ¿Dónde comandos recíprocos pueden usarse muy trivial para revertir cualquier acción tomada

el patrón de comando puede ser un mejor ajuste [pero no necesariamente, va a depender en gran medida de la situación]. En otros casos, me gustaría utilizar el patrón de recuerdo.

que probablemente se abstengan de utilizar un mashup de los dos porque tienden a preocuparse por el desarrollador que va a venir detrás de mí y mantener mi código, así como que sea mi responsabilidad ética de mi empleador para que el proceso sea lo más sencillo y barato posible. Veo un mashup de los dos patrones convertirse fácilmente un agujero de rata unmaintainable de incomodidad que sería caro de mantener.

Otros consejos

La práctica clásica es seguir la patrón Comando .

Puede encapsular cualquier objeto que realiza una acción con un comando, y tienen que realizar la acción inversa con un método Deshacer (). Almacenar todas las acciones en una pila de una manera fácil de rebobinado a través de ellos.

Hay tres enfoques aquí que son viables. Patrón memento (instantáneas), patrón de Comando y Estado diffing. Todos tienen ventajas y desventajas.

Me gustaría ir con diffing Estado si puede salirse con la suya, ya que combina la reducción de memoria con facilidad de implementación y mantenimiento .

Tenga en cuenta que la VoxelShop mencionado en el artículo es de código abierto. Para que pueda echar un vistazo a la complejidad del patrón de comandos aquí: https: // github.com/simlu/voxelshop/tree/develop/src/main/java/com/vitco/app/core/data/history

A continuación se muestra extracto del artículo:

patrón Memento

introducir descripción de la imagen aquí

Pros

  • La implementación es independiente de la acción aplicada. Una vez implementado podemos añadir acciones sin preocuparse de romper la historia.
  • Es rápido para avanzar a una posición predefinida en la historia. Esto es interesante cuando las acciones aplicadas entre la posición actual y el deseado historia son computacionalmente caro.

Contras

    Requisitos
  • La memoria puede ser significativamente más alta en comparación con otros enfoques.
  • El tiempo de carga puede ser lento si las instantáneas son grandes.

Patrón de comandos

introducir descripción de la imagen aquí

Pros

  • huella de memoria es pequeña. Tan sólo hay que guardar los cambios en el modelo y si éstos son pequeños, entonces la pila de la historia también es pequeño.

Contras

  • No se puede ir a una posición arbitraria directamente, sino que necesita un-solicitar la pila de la historia hasta llegar allí. Esto puede llevar mucho tiempo.
  • Cada acción y es inversa necesita ser encapsulada en un objeto. Si su acción es no trivial esto puede ser difícil. Los errores en la acción (inverso) son muy difíciles de depurar y pueden fácilmente resultar en accidentes fatales. Incluso las acciones que buscan simples implican generalmente una buena cantidad de complejidad. P.ej. en el caso del editor de 3D, el objeto para añadir al modelo necesita almacenar lo que se añade, de qué color fue seleccionado en ese momento, lo que se ha sobrescrito, si el modo de espejo activo, etc.
  • Puede ser difícil de implementar y mucha memoria cuando las acciones no tienen un reverso sencilla, por ejemplo, cuando una imagen borrosa.

Estado diffing

introducir descripción de la imagen aquí

Pros

  • La implementación es independiente de la acción aplicada. Una vez que se ha añadido la funcionalidad de la historia podemos añadir acciones sin preocuparse de romper la historia.
  • Requisitos
  • La memoria es generalmente mucho menor que para el enfoque de fotografía y en muchos casos comparables al método del patrón de comando. Sin embargo, esto depende en gran medida del tipo de las acciones aplicadas. P.ej. invirtiendo el color de una imagen utilizando el patrón de comando debe ser muy barato, mientras que diffing Estado ahorraría toda la imagen. Por el contrario en la elaboración de una larga línea de forma libre, el enfoque del modelo de comando podría utilizar más memoria si encadenado entradas del historial para cada píxel.

Contras / Limitaciones

  • No podemos ir a una posición arbitraria directamente, sino que necesita un-solicitar la pila de la historia hasta llegar allí.
  • necesitamos calcular el diff entre los estados. Esto puede ser costoso.
  • Implementar el diff XOR entre los estados modelo podría ser difícil de aplicar en función del modelo de datos.

Referencia:

https://www.linkedin.com/pulse / de historia de la solución de problemas de difícil-Lukas-Siemon

Tome un vistazo a la Comando Patrón . Usted tiene que encapsular cada cambio en el modelo en objetos de comando separados.

escribí un sistema muy flexible para realizar un seguimiento de los cambios. Tengo un programa de dibujo que implementa 2 tipos de cambios:

  • añadir / eliminar una forma
  • cambio de propiedad de una forma

Clase base:

public abstract class Actie
{
    public Actie(Vorm[] Vormen)
    {
        vormen = Vormen;
    }

    private Vorm[] vormen = new Vorm[] { };
    public Vorm[] Vormen
    {
        get { return vormen; }
    }

    public abstract void Undo();
    public abstract void Redo();
}

clase derivada para añadir formas:

public class VormenToegevoegdActie : Actie
{
    public VormenToegevoegdActie(Vorm[] Vormen, Tekening tek)
        : base(Vormen)
    {
        this.tek = tek;
    }

    private Tekening tek;
    public override void Redo()
    {
        tek.Vormen.CanRaiseEvents = false;
        tek.Vormen.AddRange(Vormen);
        tek.Vormen.CanRaiseEvents = true;
    }

    public override void Undo()
    {
        tek.Vormen.CanRaiseEvents = false;
        foreach(Vorm v in Vormen)
            tek.Vormen.Remove(v);
        tek.Vormen.CanRaiseEvents = true;
    }
}

clase derivada para la eliminación de formas:

public class VormenVerwijderdActie : Actie
{
    public VormenVerwijderdActie(Vorm[] Vormen, Tekening tek)
        : base(Vormen)
    {
        this.tek = tek;
    }

    private Tekening tek;
    public override void Redo()
    {
        tek.Vormen.CanRaiseEvents = false;
        foreach(Vorm v in Vormen)
            tek.Vormen.Remove(v);
        tek.Vormen.CanRaiseEvents = true;
    }

    public override void Undo()
    {
        tek.Vormen.CanRaiseEvents = false;
        foreach(Vorm v in Vormen)
            tek.Vormen.Add(v);
        tek.Vormen.CanRaiseEvents = true;
    }
}

clase derivada de los cambios de propiedad:

public class PropertyChangedActie : Actie
{
    public PropertyChangedActie(Vorm[] Vormen, string PropertyName, object OldValue, object NewValue)
        : base(Vormen)
    {
        propertyName = PropertyName;
        oldValue = OldValue;
        newValue = NewValue;
    }

    private object oldValue;
    public object OldValue
    {
        get { return oldValue; }
    }

    private object newValue;
    public object NewValue
    {
        get { return newValue; }
    }

    private string propertyName;
    public string PropertyName
    {
        get { return propertyName; }
    }

    public override void Undo()
    {
        //Type t = base.Vorm.GetType();
        PropertyInfo info = Vormen.First().GetType().GetProperty(propertyName);
        foreach(Vorm v in Vormen)
        {
            v.CanRaiseVeranderdEvent = false;
            info.SetValue(v, oldValue, null);
            v.CanRaiseVeranderdEvent = true;
        }
    }
    public override void Redo()
    {
        //Type t = base.Vorm.GetType();
        PropertyInfo info = Vormen.First().GetType().GetProperty(propertyName);
        foreach(Vorm v in Vormen)
        {
            v.CanRaiseVeranderdEvent = false;
            info.SetValue(v, newValue, null);
            v.CanRaiseVeranderdEvent = true;
        }
    }
}

Con cada vez Vormen = la matriz de elementos que se someten al cambio. Y debe ser utilizado de esta manera:

Declaración de las pilas:

Stack<Actie> UndoStack = new Stack<Actie>();
Stack<Actie> RedoStack = new Stack<Actie>();

Adición de una nueva forma (por ejemplo. Point)

VormenToegevoegdActie vta = new VormenToegevoegdActie(new Vorm[] { NieuweVorm }, this);
UndoStack.Push(vta);
RedoStack.Clear();

Extracción de una forma seleccionada

VormenVerwijderdActie vva = new VormenVerwijderdActie(to_remove, this);
UndoStack.Push(vva);
RedoStack.Clear();

El registro de un cambio de propiedad

PropertyChangedActie ppa = new PropertyChangedActie(new Vorm[] { (Vorm)e.Object }, e.PropName, e.OldValue, e.NewValue);
UndoStack.Push(ppa);
RedoStack.Clear();

Finalmente la acción de deshacer / rehacer

public void Undo()
{
    Actie a = UndoStack.Pop();
    RedoStack.Push(a);
    a.Undo();
}

public void Redo()
{
    Actie a = RedoStack.Pop();
    UndoStack.Push(a);
    a.Redo();
}

Creo que esta es la manera más efectiva de implementar un algoritmo de deshacer-rehacer. Por ejemplo, mira esta página en mi sitio web:. DrawIt

He implementado las cosas deshacer rehacer en torno a la línea 479 de los Tekening.cs de archivo. Puede descargar el código fuente. Puede ser implementado por cualquier tipo de aplicación.

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