我正在编写自己的'魔方'应用程序。主类Cube
有18种旋转方法:
RotateAxisZClockWise,RotateAxisZAntiClockWise
RotateUpperFaceClockWise,RotateUpperFaceAntiClockWise
是的,它们可以成对加入一个参数Direction(例如RotateFrontFace(Direction direction)
),但是现在这看似合适。
我想实现撤消/重做功能,因为所有方法都具有相同的签名(没有输入参数,void返回类型),所以它们可以保存在LinkedList数据结构中。因此,每次调用其中一个旋转方法时,都会将其添加到链接列表中。
如果我们从LinkedList的开头开始(虽然还没有尝试过)并且向前推进,这将非常有效,因此每次旋转都将完全按照首先执行。
但撤消呢?如果我遍历列表从结尾到beginnig,那么应该调用相反的方法(例如,而不是RotateFrontFaceClockWise
,应该调用RotateFrontFaceAntiClockWise
)。任何想法如何实现这一点?优雅? :)
答案 0 :(得分:1)
如果其中一个主要目的是能够执行重做/撤消,我不会使用委托引用作为轮换建模的方法。我会考虑为每个旋转创建一个数据模型,并存储这些旋转步骤的列表。然后,每个步骤都可以拥有自己的相关重做/撤消委托,允许有人遍历列表(从任一端)来了解发生了什么操作,并重复或反转它们。
以数据为导向的方法来模拟此类转换的另一个好处是,它可能会减少RotateXXX( )
方法的类似(但略有不同)版本的数量。
编辑:解决有关此类解决方案可能采取何种形式的问题。
最简单的方法是将Tuple<Action,Action>
存储为代表每对旋转/非旋转操作作为配对代理。但是,我会考虑使用描述旋转操作的显式数据结构,最终可能包括描述性名称,方向/面部属性等内容。我还会更改您的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 );
}
答案 1 :(得分:1)
我知道你想避免参数化你的方法,但你可能最好不要追求这些方面:
enum Face
{
Top,
Bottom,
Left,
Right,
Front,
Back
}
enum Direction
{
Clockwise,
CounterClockwise
}
struct Rotation
{
public Face Face;
public Direction Direction;
}
LinkedList<Rotation> actions;
您可以选择是将直接操作还是反向操作推送到列表中,但是一旦以这种方式存储操作,就很容易编写一些快速切换语句来反转操作,因为它从列表中删除
在旁注中,考虑用Stack替换LinkedList,它也会为您提供服务,并且它非常适合此目的。
编辑:
注意到我的枚举中没有轴旋转的支持,但我确定你可以在Face枚举中添加它们作为附加条目(尽管你可能希望在那时重命名)
答案 2 :(得分:1)
18个方法似乎要跟上很多,特别是在考虑实现撤消/重做功能时。您是否考虑过使用单一,更通用的方法?如果这样做,您可以以非常统一的方式存储传递给该方法的内容,并轻松执行相反的操作。可以从字典中查找相反的动作。
答案 3 :(得分:0)
假设你真的想坚持你的模特......
您可以尝试在列表中输入所执行方法的每个名称,然后通过反射,通过迭代列表来调用计数器部分。这假设您的方法将具有匹配的名称(如您所建议的那样)。
您还可以创建一个HybridDictionary,您可以将方法名称作为计数器方法的ID和地址作为值。因此,当您遍历列表时,您可以让委托处理给定值的方法。