使用 LinkedList<Action> 撤消/重做功能执行 [英] Undo/redo functionality with LinkedList&lt;Action&gt; implementation

查看:29
本文介绍了使用 LinkedList<Action> 撤消/重做功能执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写自己的魔方"应用程序.主类Cube有18种旋转方法:

I am writing my own 'Rubik's cube' application. The main class Cube has 18 rotation methods:

  • RotateAxisXClockWise、RotateAxisXAntiClockWise
  • RotateAxisYClockWise、RotateAxisYAntiClockWise
  • RotateAxisZClockWise、RotateAxisZAntiClockWise

  • RotateAxisXClockWise, RotateAxisXAntiClockWise
  • RotateAxisYClockWise, RotateAxisYAntiClockWise
  • RotateAxisZClockWise, RotateAxisZAntiClockWise

RotateUpperFaceClockWise,RotateUpperFaceAntiClockWise

RotateUpperFaceClockWise, RotateUpperFaceAntiClockWise

是的,它们可以与参数方向成对连接(例如 RotateFrontFace(Direction direction)),但现在这似乎是合适的.

Yes, they could be joined in pairs with a parameter Direction (for example RotateFrontFace(Direction direction)) but for now this seems appropriately.

我想实现撤消/重做功能,因为所有方法都具有相同的签名(无输入参数,返回类型为空),它们可以保存在 LinkedList 数据结构中.所以每次调用其中一种旋转方法时,都会将其添加到链表中.

I would like to implement undo/redo functionality and because all methods have the same signature (no input parameters, void return type) they could be saved in a LinkedList data structure. So every time one of the rotation methods is called, it is added to the linked list.

如果我们从 LinkedList 的开头开始(虽然还没有尝试过)并朝最后推进,那么这将非常有效,因此每次旋转都将完全按照最初的方式执行.

This would work pretty well if we start on the beginning of the LinkedList (haven't tried it out yet though) and advance toward the end, so each rotation would be performed exactly as it was in the first place.

但是撤销呢?如果我从末尾遍历列表到开头,则应调用相反的方法(例如,应调用 RotateFrontFaceAntiClockWise 而不是 RotateFrontFaceClockWise,而是调用).任何想法如何实现这一点?优雅?:)

But what about undo? If I traverse the list from the end to the beginnig, then the opposite method should be called (for example instead of RotateFrontFaceClockWise, RotateFrontFaceAntiClockWise should be called). Any ideas how to implement this? Elegantly? :)

推荐答案

我不会使用委托引用作为旋转建模的方式,如果主要目的之一是能够执行重做/撤消.我会考虑为每个旋转创建一个数据模型,并存储这些旋转步骤的列表.每个步骤都可以有它自己关联的重做/撤消委托,这允许某人(从任一端)遍历列表以了解发生了什么操作,并重复或反转它们.

I would not use delegate references as the way to model the rotations, if one of the main purposes is to be able to perform redo/undo. I would consider creating a data-model for each rotation, and store a list of these rotation steps. Each step could then have it's own associated Redo/Undo delegate, which allows someone traversing the list (from either end) to understand what operations took place, and either repeat or reverse them.

面向数据的方法对此类转换进行建模的另一个好处是,它可能会减少 RotateXXX( ) 方法的类似(但略有不同)版本的数量.

One additional benefit of a data-oriented approach to model such transformations, is that it could potentially reduce the number of similar (but slightly different) versions of your RotateXXX( ) methods.

解决您关于此类解决方案可能采取何种形式的问题.

要做的最简单的事情可能是存储一个 Tuple,将每对旋转/不旋转操作表示为成对的委托.但是,我会考虑使用描述旋转操作的显式数据结构,可能最终包括诸如描述性名称、方向/面属性等内容.我还将更改您的 RotateXXX 方法,使它们成为 Cube 的静态方法,并接受一个立方体实例作为参数.这将允许在 Cube 实例的外部对旋转操作进行建模.

The simplest thing to do may be to store a Tuple<Action,Action> representing each pair of rotate/unrotate operations as paired delegates. However, I would consider using an explicit data structure that describes the rotation operation, perhaps eventually including things like a descriptive name, direction/face attributes, and so on. I would also change your RotateXXX methods so that they are static methods of Cube, and accept an instance of cube as a parameter. This would allow modeling the rotation operations externally to the instance of Cube.

public sealed class Rotation
{
    private readonly Action<Cube> _RotateAction;
    private readonly Action<Cube> _UnrotateAction;  // used for undo or backtracking

    private Rotation( Action<Cube> rotateAction, Action<Cube> unrotateAction )
    {
        _RotateAction = rotateAction;
        _UnrotateAction = unrotateAction;
    }

    public void Rotate( Cube cube )   { _RotateAction( cube ); }

    public void Unrotate( Cube cube ) { _Unrotate( cube ); }

    public static readonly RotateFrontFaceClockswise = 
        new Rotation( Cube.RotateFrontFaceClockwise
                      Cube.RotateFrontFaceCounterClockwise );

    public static readonly RotateFrontFaceCounterClockwise = 
        new Rotation( Cube.RotateFrontFaceCounterClockwise,
                      Cube.RotateFrontFaceClockwise );

    public static readonly RotateLeftFaceClockwise = 
        new Rotation( Cube.RotateLeftFaceClockwise,
                      Cube.RotateLeftFaceCounterClockwise );

    public static readonly RotateLeftFaceCounterClockwise = 
        new Rotation( Cube.RotateLeftFaceCounterClockwise,
                      Cube.RotateLeftFaceClockwise );
    // etc..
}

// now we can keep track of the state changes of a cube using:
List<Rotation> cubeRotations = new List<Rotation>();
cubeRotations.Add( Rotation.RotateFrontFaceCounterClockwise );
cubeRotations.Add( Rotation.RotateBackFaceClockwise );
cubeRotations.Add( Rotation.RotateLeftFaceCounterClockwise );

// to apply the rotations to a cube, you simple walk through the data structure
// calling the Rotate( ) method on each:
Cube someCube = new Cube( ... )
foreach( Rotation r in cubeRotations )
{
    r.Rotate( someCube );
}

// to undo these rotations you can walk the like in reverse:
foreach( Rotation r in cubeRotations.Reverse() )
{
    r.Unrotate( someCube );
}

这篇关于使用 LinkedList<Action> 撤消/重做功能执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆