【Java】アンドゥ・リドゥ機能の作成

By | 2011年10月12日

ツール系アプリにあると何かと便利なアンドゥ・リドゥ機能の実装について考えてみる。
こういった機能は汎用的に作っておくと使い回しが利くのでクラスにしておくと良い。
このクラスに最低限必要なのは、今までに行った操作を順に覚えておくことと、適切に操作の戻し/進みが出来ることである。
操作を一つのリンクトリストで管理してもいいのだが、アンドゥ用とリドゥ用の2つのスタックで管理すると
シンプルに実装できるのではないだろうか。
次に、必要なメソッドを考えてみる。

  • アンドゥ() ・・・ 操作を一つ戻す
  • アンドゥのスタックに要素があるならば、popし、リドゥのスタックにpushして要素を返す。

  • リドゥ() ・・・ 戻した操作をもう一度元に戻す
  • リドゥのスタックに要素があるならば、popし、アンドゥのスタックにpushして要素を返す。

  • 履歴の追加() ・・・ 行った操作を履歴に追加する
  • アンドゥのスタックに操作をpushし、リドゥのスタックはクリアする。

  • 履歴一覧の取得() ・・・ 履歴の一覧を取得する
  • アンドゥのスタックの一覧を返す。

以上で最低限の機能は満たせそうである。
これらの機能をコードにすると以下のようになる。

HistoryStack.java

package com.lesson.undo;

import java.util.Stack;

public class HistoryStack<T> {

	private final Stack<T> undoStack = new Stack<T>();
	private final Stack<T> redoStack = new Stack<T>();
	
	/**
	 * アンドゥ
	 * @return
	 */
	public T undo(){

		T result = null;
		if( !undoStack.empty() ){
			result = undoStack.pop();
			redoStack.push(result);
		}

		return result;
	}

	/**
	 * リドゥ
	 * @return
	 */
	public T redo(){

		T result = null;
		if( !redoStack.empty() ){
			result = redoStack.pop();
			undoStack.push(result);
		}

		return result;
	}

	/**
	 * 履歴の追加
	 * @param history
	 */
	public void add(T history){
		undoStack.push(history);
		redoStack.clear();
	}
	
	/**
	 * アンドゥの列挙
	 * @return
	 */
	public final Iterable<T> iterateUndo(){
		return undoStack;
	}
}

次に、このクラスを使ったテストプログラムを書いてみよう。

Main.java

package com.lesson.undo;

public class Main {
	
	/**
	 * Iterableのプリント
	 * @param ite
	 */
	private static <T> void printIterable(final Iterable<T> ite){
		for(T i:ite){
			System.out.print(i + " ");
		}
		System.out.println();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		HistoryStack<Integer> history = new HistoryStack<Integer>();
		
		// 1~5まで履歴を追加
		history.add(1);
		history.add(2);
		history.add(3);
		history.add(4);
		history.add(5);
		
		System.out.println("1~5まで履歴を追加");
		printIterable(history.iterateUndo());
		
		// 2回undoする
		history.undo();
		history.undo();
		
		System.out.println("2回undoする");
		printIterable(history.iterateUndo());
		
		// 1回redoする
		history.redo();
		
		System.out.println("1回redoする");
		printIterable(history.iterateUndo());
		
		// 履歴6を追加
		history.add(6);
		
		System.out.println("履歴6を追加");
		printIterable(history.iterateUndo());
	}
}

このテストプログラムを実行すると以下のように表示される。

1~5まで履歴を追加
1 2 3 4 5
2回undoする
1 2 3
1回redoする
1 2 3 4
履歴6を追加
1 2 3 4 6

アンドゥ・リドゥ機能が正しく働いていることが分かる。

今回作ったHistoryStackクラスはジェネリクスを用いて、どんな型でも格納できるようにしてある。
テストプログラムでは簡単のためInteger型を用いたが、実際には操作を表すコマンドクラスのようなものを作って
格納していくことになる。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です