Отменить / повторить функциональность с реализацией LinkedList <Action> - PullRequest
0 голосов
/ 28 февраля 2011

Я пишу свое собственное приложение «Кубик Рубика».Основной класс Cube имеет 18 способов поворота:

  • RotateAxisXClockWise, RotateAxisXAntiClockWise
  • RotateAxisYClockWise, RotateAxisYAntiClockWise
  • RoiseAxAxAxAxis0AXA1AXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXAXT

  • RotateUpperFaceClockWise, RotateUpperFaceAntiClockWise

  • RotateDownFaceClockWise, RotateDownFaceAntiClockWise

Да, их можно объединить в пары с параметром Direction (например, RotateFrontFace(Direction direction)), но сейчас это кажется подходящим.

Я бынравится реализовывать функции отмены / повторения и поскольку все методы имеют одинаковую сигнатуру (без входных параметров, тип возврата void), они могут быть сохранены в структуре данных LinkedListПоэтому каждый раз, когда вызывается один из методов ротации, он добавляется в связанный список.

Это будет работать очень хорошо, если мы начнем с начала LinkedList (пока еще не пробовал его) и продвинемся к концу, так что каждый поворот будет выполняться точно так, как это было в первую очередь..

Но как насчет отмены?Если я перейду список от конца к началу, то должен быть вызван противоположный метод (например, вместо RotateFrontFaceClockWise, должен быть вызван RotateFrontFaceAntiClockWise).Есть идеи, как это реализовать?Элегантно?:)

Ответы [ 4 ]

1 голос
/ 01 марта 2011

18 методов, похоже, не отстают, особенно если учесть реализацию функциональности отмены / возврата.Рассматривали ли вы использовать один, более общий метод?Если бы вы это сделали, вы могли бы хранить то, что было передано этому методу, очень равномерно и легко выполнять противоположные действия.Противоположные действия можно найти в словаре.

1 голос
/ 28 февраля 2011

Я знаю, что вы хотели избежать параметризации своих методов, но вам, вероятно, лучше всего заняться чем-то подобным:

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

enum Direction
{
    Clockwise,
    CounterClockwise
}

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

LinkedList<Rotation> actions;

Вы можете выбрать, добавлять ли прямое или обратное действие в список, но после того, как вы сохраните действия таким образом, очень легко написать несколько операторов быстрого переключения для отмены действия, когда оно удаляется из списка. .

Обратите внимание, что замените LinkedList на Stack, он точно так же будет обслуживать вас и идеально подходит для этой цели.

EDIT:

Заметил, что у меня нет поддержки вращения осей в моих перечислениях, но я уверен, что вы могли бы добавить их как дополнительные записи в перечислении Face (хотя вы можете переименовать его в этой точке)

1 голос
/ 28 февраля 2011

Я бы не использовал ссылки делегатов в качестве способа моделирования вращений , если одна из основных целей состоит в том, чтобы иметь возможность выполнять повтор / отмену.Я хотел бы рассмотреть возможность создания модели данных для каждой ротации и сохранить список этих шагов ротации.Каждый шаг может иметь свой собственный связанный делегат Redo / Undo, который позволяет кому-либо просматривать список (с любого конца), чтобы понять, какие операции были выполнены, и либо повторить, либо отменить их.

Одно дополнительное преимущество данных-ориентированный подход к моделированию таких преобразований заключается в том, что он потенциально может уменьшить количество похожих (но немного отличающихся) версий ваших 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 );
}
0 голосов
/ 28 февраля 2011

Предполагая, что вы действительно хотите придерживаться своей модели ...

Вы можете попробовать, введя в список каждое имя выполняемого метода, а затем, с отражением, вызвать счетчики, перебирая список.,Это предполагает, что ваши методы будут иметь совпадающие имена (как вы предложили).

Вы также можете создать HybridDictionary , в котором вы будете использовать имена методов в качестве идентификатора и адреса метода счетчика в качествезначение.И поэтому, когда вы перебираете список, у вас может быть делегат, обрабатывающий метод с заданным значением.

...