デザインパターン入門 | Memento(メメント)パターン

「Memento(メメント)」パターンは、ある時点のアプリケーションの「内部情報」を保存することができるデザインパターンです。

保存した「内部情報」を元にアプリケーションの状態を復元することもできます。

今回はCUIベースのシンプルなテキストアプリケーションを元に「Memento(メメント)」パターンについてお話をしていきたいと思います。

「Memento(メメント)」の役割

「Memento(メメント)」とは、「記念碑・形見」という意味がありますが、今回はアプリケーションの「内部情報」を表すクラスとして「Memento(メメント)」クラスを作成していきます。

今回は「Memento(メメント)」クラスをシンプルなテキストアプリケーションの「内部情報」を保存するために利用していきます。

アプリケーションに登録されたデータは「ArrayList」で管理していきたいと思いますので、下記のような内容 で作成することにしました。

public class Memento {
	ArrayList text;
	
	// コンストラクタ
	Memento(ArrayList text){
		this.text = text;
	}
}

「ArrayList」型のフィールドを作成し、コンストラクタで受け取った引数をこのフィールドに設定をしています。

では、次にシンプルな「テキストアプリケーション」の全体について見ていきたいと思います。

シンプルな「テキストアプリケーション」

今回は、

  • テキストの登録
  • テキストの表示
  • アンドゥ(Undo)
  • リドゥ(Redo)

の機能を持った「CUIベース」の「テキストアプリケーション」を作っていきたいと思います。

今回作るクラスの関係は下図のようになります。

メメント1

「Main」クラスは処理の流れを決定する役割を持っていますが、このクラスの「main」メソッドの中には「Memento」クラス型の変数が用意されています。

この変数に「現在のアプリケーションの状態(内部情報)」を保存しておくことで、必要な時に保存した「内部情報」を利用し、アプリケーションの状態を復元することができます。

そして、アプリケーション内のテキストは「TextManager」クラスで管理していきます。

メメント2

「TextManager」クラスは下記のようになります。

public class TextManager {
	ArrayList textPool = new ArrayList(); // 現在登録されているテキスト
	String registText = null; // 登録テキスト
	boolean redoFlg = false; // リドゥフラグ

    // テキスト登録
	public void registerText(Scanner scan) {
		boolean registerFlg = true;

		while ( registerFlg ) {
			System.out.println("登録するテキストを入力してください。");
			System.out.print("->");
			String input = scan.nextLine();
			if( input.equals("") ) {
				System.out.println("テキストが入力されていません。");
			} else {
				textPool.add(input);
				registText = input;
				System.out.println("テキスト「" + input + "」を登録しました。");
				registerFlg = false;
			}
		}
	}

	// 登録テキスト表示
	public void displayText() {
		System.out.println("===== 登録テキスト =====");
		Iterator it = textPool.iterator();
		while ( it.hasNext() ) {
			String str = (String)it.next();
			System.out.println(str);
		}
		System.out.println("========================");
	}

	// 現在の状態のMementoを生成
	public Memento createMemento() {
		return new Memento((ArrayList) this.textPool.clone());
	}

	// UNDO
	public void undo(Memento memento) {
		this.textPool = memento.text;
		redoFlg = true;
		System.out.println("アンドゥを行いました。");
	}

	// REDO
	public void redo() {
		if ( redoFlg == true ) {
			textPool.add(registText);
			registText = null;
			redoFlg = false;
			System.out.println("リドゥを行いました。");
		} else {
			System.out.println("リドゥできません。");
		}
	}
}

「アンドゥ(UNDO)」機能の部分で、引数に渡された「Memento(メメント)」からアプリケーションのテキストを復元しています。

「リドゥ(REDO)」は、直前のアンドゥ(UNDO)動作の取り消しにのみ対応しています。

そして、「Main」クラスの内容は下記のようになります。/p>

public class Main {
	public static void main(String[] args) {
		TextManager tm = new TextManager();
		String input;
		boolean processFlg = true;
		Scanner scan = new Scanner(System.in);
		Memento memento = tm.createMemento();

		while( processFlg ) {
			System.out.println("どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了");
			input = scan.nextLine();
			if ( input.equals("r") ) {
				System.out.println("テキスト登録を行います。");
				memento = tm.createMemento(); // テキスト登録前の状態を保存
				tm.registerText(scan);
			} else if ( input.equals("d") ) {
				System.out.println("テキスト表示を行います。");
				tm.displayText();
			} else if ( input.equals("u") ) {
				tm.undo(memento);
			} else if ( input.equals("rd") ) {
				tm.redo();
			} else if ( input.equals("f") ) {
				processFlg = false;
				System.out.println("プログラムを終了しました。");
			} else {
				System.out.println("入力値に誤りがあります。");
			}
		}
		scan.close();;
	}
}

「Main」クラスの「main」メソッドの中には「Memento」型の変数があり、この変数にアプリケーションが保存しているテキストの状態を保存しています。

テキストを登録する度に、現在のテキストの状態を「Memenoto」に保存してから、テキストの登録を行うことで1つ前のテキストの状態を復元することができるようになっています。

プログラムを実行した結果は下記のようになります。

                
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
r
テキスト登録を行います。
登録するテキストを入力してください。
->おはようございます。
テキスト「おはようございます。」を登録しました。
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
r
テキスト登録を行います。
登録するテキストを入力してください。
->こんにちは
テキスト「こんにちは」を登録しました。
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
r
テキスト登録を行います。
登録するテキストを入力してください。
->こんばんは
テキスト「こんばんは」を登録しました。
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
d
テキスト表示を行います。
===== 登録テキスト =====
おはようございます。
こんにちは
こんばんは
========================
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
u
アンドゥを行いました。
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
d
テキスト表示を行います。
===== 登録テキスト =====
おはようございます。
こんにちは
========================
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
rd
リドゥを行いました。
■どの処理を行いますか?[r]:テキスト登録 [d]:テキスト表示 [u]アンドゥ [rd]リドゥ [f]プログラム終了
d
テキスト表示を行います。
===== 登録テキスト =====
おはようございます。
こんにちは
こんばんは
========================

このように「Memento(メメント)」パターン」は、アプリケーションの状態を保存して、保存した時点へアプリケーションの状態を復元することができるデザインパターンです。

ファイルやデータベースに「Mementoの状態」を保存するようにすれば、アプリケーションを終了しても元の状態に復元することもできます。

「Memento(メメント)」パターン」の仕組みを理解して、自分で作成したアプリケーションに適用できるようになっていきましょう。

HOMEへ