UndoとRedoの実装
09/17 2020
コマンドパターンの実装
/// <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();
}
}