Undo,Redoの実装つづき

いや、悪い反応では無いのでむしろ喜ぶべきことなんだけど、意外と好評なエントリーなので続きを書きます。
タイトルだけは前回同様釣りっぽいタイトルで^^
前回のエントリーが思ったより反響が大きくてびっくりしてます。なんか炎上してるのか?って思っちゃいましたw
炎上っていうのはさすがにネガティブだし、内容とかけ離れすぎてるのでタイトル変えました。


さて前回はコマンドパターンとコマンドの実装について書きましたが、今回は予告どおり実行部の実装についてです。
といっても、今回はとっても単純な話です。Undo用にスタックにコマンドを積んで、Undoするよって呼ばれたら、スタックからコマンドをpopしてきてundoの処理を実行するだけです。
ただし、Redoも出来ないと行けないので、実行したコマンドは今度はRedo用のスタックに積んでいきます。
実装は次のようになります。


まず前回の二番目のパターンに則って、ICommandインタフェースがあるとします。

public interface ICommand {
    public void invoke();
    public void undo();
    public void redo();
}


コマンドの実行部は次のようになります。

public class CommandInvoker {
    private Stack<ICommand> mUndoBuffer;
    private Stack<ICommand> mRedoBuffer;

    public CommandInvoker() {
         mUndoBuffer = new Stack<ICommand>();
         mRedoBuffer = new Stack<ICommand>();
    }

    public void invoke(ICommand command) {
        command.invoke();
        mRedoBuffer.clear();
        mUndoBuffer.push(command);
    }
    
    public void undo() {
        if (mUndoBuffer.isEmpty()) {
             return;
        }
        ICommand command = mUndoBuffer.pop();
	command.undo();
	mRedoBuffer.push(command);
    }

    public void redo() {
        if (mRedoBuffer.isEmpty()) {
	    return;
	}
        ICommand command = mRedoBuffer.pop();
	command.redo();
	mUndoBuffer.push(command);
    }
}


こんな感じです。


ここではポリモーフィズムを使った実装のほうを用いましたが、ブックマークにこんなコメントが残っていました。

id:kawacho 全コマンドでundo, redo実装するのはアレだしコマンドのインスタンスが積まれるのもアレなので、編集情報を表すやつを積んでいくとか。

これは前回のエントリーでわたしが説明した一つ目の実装です。ブコメの中のアレというのが何を指しているのか分かりませんが、もちろんこちらの実装もありです。
この辺はデータモデルに依存する話なのだと思います。前回のわたしの解説ではコマンドオブジェクトをキャストしていましたが、編集情報を一つの型で表すことが出来るならキャストする必要はありません。
その場合はおそらく実装としても編集情報だけをコマンドオブジェクトに持たせて、実際の処理はまとまったところにある方がわかりやすいでしょう。
例えば、テキストエディタのような文字列だけの編集情報だとか、テーブルやリストといったものは編集情報は限られていて一つの型に封じ込めることも可能です。わたしも昔ツリー型のデータ構造に対してそのような実装をしていました。
ただ、コマンド側に処理を直接書けるというのは非常に柔軟だと思います。わたしが当時作っていたのはフレームワークでしたので、あらかじめ必要な編集操作というのをすべて想定して作っていたのでコマンドに編集情報だけを持たせるという方法もありでした。しかし、操作方法が変更になったりするような場合はコマンド側に処理を記述した方がより柔軟に対処できると思います。


Undo,RedoだけでなくGUIの編集系のソフトウェアについていろいろ書きたいのでまだまだ続きますよ!