0

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

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

  • RotateUpperFaceClockWise, RotateUpperFaceAntiClockWise

  • RotateFrontFaceClockWise, RotateFrontFaceAntiClockWise
  • RotateRightFaceClockWise, RotateRightFaceAntiClockWise
  • RotateBackFaceClockWise, RotateBackFaceAntiClockWise
  • RotateLeftFaceClockWise, RotateLeftFaceAntiClockWise
  • RotateDownFaceClockWise, RotateDownFaceAntiClockWise

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

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

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

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

4

4 回答 4

1

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

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

编辑:解决您关于这种解决方案可能采取何种形式的问题。

最简单的做法可能是将Tuple<Action,Action>代表每对旋转/取消旋转操作的 a 存储为成对的委托。但是,我会考虑使用描述旋转操作的显式数据结构,可能最终包括描述性名称、方向/面属性等。我还将更改您的RotateXXX方法,使其成为 的静态方法Cube,并接受多维数据集的实例作为参数。这将允许在 的实例外部对旋转操作进行建模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 );
}
于 2011-02-28T20:52:19.600 回答
1

我知道你想避免参数化你的方法,但你可能最好遵循这些思路:

enum Face
{
    Top,
    Bottom,
    Left,
    Right,
    Front,
    Back
}

enum Direction
{
    Clockwise,
    CounterClockwise
}

struct Rotation
{
     public Face Face;
     public Direction Direction;
}

LinkedList<Rotation> actions;

您可以选择是将直接操作还是反向操作推送到列表中,但是一旦以这种方式存储操作,就很容易编写一些快速 switch 语句来反转从列表中删除的操作。

附带说明一下,考虑用 Stack 替换 LinkedList,它也会为您提供服务,并且非常适合此目的。

编辑:

注意到我的枚举中不支持轴旋转,但我相信您可以将它们作为附加条目添加到 Face 枚举中(尽管此时您可能想要重命名它)

于 2011-02-28T20:52:46.753 回答
1

18 种方法似乎需要跟上,尤其是当您考虑实现撤消/重做功能时。您是否考虑过使用单一的、更通用的方法?如果这样做,您可以以非常统一的方式存储传递给该方法的内容,并轻松执行相反的操作。可以从字典中查找相反的动作。

于 2011-02-28T21:44:09.187 回答
0

假设你真的想坚持你的模型......

您可以尝试通过在列表中输入所执行方法的每个名称,然后通过反射,通过遍历列表来调用计数器部分。这假设您的方法将具有匹配的名称(如您所建议的那样)。

您还可以创建一个HybridDictionary,在其中使用方法名称作为 id,将计数器方法的地址作为值。因此,当您遍历列表时,您可以让委托处理给定值的方法。

于 2011-02-28T20:54:00.990 回答