UndoとRedoの実装


コマンドパターンの実装

/// <summary>
/// コマンド抽象クラス
/// </summary>
public abstract class Command
{
    /// <summary>
    /// コマンドを適用する実装
    /// </summary>
    protected abstract void ApplyImpl();

    /// <summary>
    /// コマンド適用を元に戻す実装
    /// </summary>
    protected abstract void RevertImpl();

    /// <summary>
    /// コマンドを適用する
    /// </summary>
    public void Apply()
    {
        ApplyImpl();
    }

    /// <summary>
    /// コマンド適用を元に戻す
    /// </summary>
    public void Revert()
    {
        RevertImpl();
    }
}

コマンド履歴管理

/// <summary>
/// 操作履歴
/// HACK: リングバッファで実装するともっと高速だよ
/// </summary>
public class CommandHistory
{
    //  ダミーノード用
    private class SampleCommand : Command
    {
        protected override void ApplyImpl() { }
        protected override void RevertImpl() { }
    }

    private readonly LinkedList<Command> commands = new LinkedList<Command>();
    private readonly LinkedListNode<Command> firstNode;
    private LinkedListNode<Command> current;

    private int Capacity { get; }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="capacity">最大保持数</param>
    public CommandHistory(int capacity)
    {
        Capacity = capacity;
        commands.AddLast(new SampleCommand());
        firstNode = commands.First;
        current = firstNode;
    }

    /// <summary>
    /// コマンドを追加する
    /// </summary>
    /// <param name="command">追加するコマンド</param>
    public void Push(Command command)
    {
        //  現在以降のコマンドを全て削除
        while (current.Next != null)
        {
            commands.RemoveLast();
        }

        commands.AddLast(command);
        current = current.Next;

        //  古いコマンドを削除
        while (commands.Count > Capacity + 1)
        {
            commands.Remove(firstNode.Next);
        }
    }

    /// <summary>
    /// アンドゥ可能か判定する
    /// </summary>
    /// <returns>true: アンドゥ可能, false: アンドゥ不能</returns>
    public bool CanUndo()
    {
        return current != firstNode;
    }

    /// <summary>
    /// リドゥ可能か判定する
    /// </summary>
    /// <returns>true: リドゥ可能, false: リドゥ不能</returns>
    public bool CanRedo()
    {
        return current.Next != null;
    }

    /// <summary>
    /// アンドゥを実行する
    /// </summary>
    public void Undo()
    {
        current.Value.Revert();
        current = current.Previous;
    }

    /// <summary>
    /// リドゥを実行する
    /// </summary>
    public void Redo()
    {
        current = current.Next;
        current.Value.Apply();
    }
}
© 2020-2021 Manicreator