第18章では Memento パターンを学びます。Memento とは、英語で「記念品」「形見」を意味する単語です。
記念品や形見を見ると、当時の状況が思い出されます。Memento パターンとは、
インスタンスのあるときの状態をスナップショットとして保存しておくことで、
その時のインスタンスの状態を復元することを可能にするものです。
インスタンスの状態が、プログラム実行中にどんどん変化することが考えられます。
一度変化してしまったインスタンスを、「少し前の状態に戻したい」「ある時点の状態に戻したい」などの要求は時に発生するものです。
このような要求にスマートに応えることができるのが、Memento パターンです。
Memento パターンを使うと、インスタンスのある時の状態を、簡単にスナップショットとして残すことができ、
さらに、そこからの復元も可能になります。インスタンス全ての状態を覚えておくために、
clone を作成することもありますが、Memento パターンでは、必要な情報のみを保持しておき、必要なデータのみを復元することを考えます。
サンプルケースでは、いくつかの数字の足し算について考えてみます。
勉強大好きの山田君と和田山君が話をしています。1から5までの数字を足したらいくつになるだろう?
気になって仕方がない二人は実際に計算してみることにしました。
1 + 2 + 3 + 4 + 5 = ?
二人とも暗算で計算し、せーので答えを言い合いました。
二人とも勉強大好きなだけに正解でした。
何日か経って、二人はまた気になることができました。
1から10までの数字を足したらいくつになるだろう?
気になった二人はまた暗算します。
山田君:1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = ?
和田山君:15 + 6 + 7 + 8 + 9 + 10 = ?
二人の計算方法に違いがあります。山田君は、再び1から順に足していきましたが、和田山君は、先ほど1から5まで足した結果を利用して10までの計算を行っています。
和田山君は、1から5までを足した値が「15」になることが思い出として残しておいたのです。ソースコードを見てみましょう。
/**
* ひとつの計算を表すクラス。
*/
public class Calc {
private int temp = 0;
/**
* 足し算を実行するメソッド
* @param plus
*/
public void plus(int plus) {
temp += plus;
}
/**
* 途中経過を Memento として取得するメソッド
* @return memento
*/
public Memento createMemento() {
return new Memento(temp);
}
/**
* Memento から計算経過を取得して、temp にセットする
* @param memento
*/
public void setMemento(Memento memento) {
this.temp = memento.result;
}
/**
* 計算結果を取得するメソッド
* @return temp
*/
public int getTemp() {
return this.temp;
}
/**
* 途中経過を保持する Memento クラス
*/
public class Memento {
/** 計算の途中経過を表す */
private int result = -1;
/**
* 計算経過を引数に受け取るコンストラクタ
* @param temp
*/
Memento(int temp) {
this.result = temp;
}
}
}
import java.util.HashMap;
import java.util.Map;
/**
* 計算する和田山君クラス。
*/
public class Wadayama {
private static Map map = new HashMap();
/**
* 計算を実行するクラス
* @param args
*/
public static void main(String args[]) {
Calc calc = new Calc();
for (int n = 1; n <= 5; n++) {
calc.plus(n);
}
System.out.println(calc.getTemp());
map.put("5までの足し算", calc.createMemento());
// 数日経過
// 10までの足し算をしたい。
Calc calc2 = new Calc();
calc2.setMemento((Calc.Memento) map.get("5までの足し算"));
for (int n = 6; n <= 10; n++) {
calc2.plus(n);
}
System.out.println(calc2.getTemp());
map.put("10までの足し算", calc2.createMemento());
}
}
このように、ある段階のものを「スナップショット」として残しておくことで、その時の状態にすばやく戻すことができます。
Memento パターンは、このように「あるときの状態」を思い出せるように「記念品」を残しておくようなパターンなのです。
サンプルケースをクラス図にしてみましょう。
このような足し算であれば、1から再計算することで、簡単にその時の値を取得することができるかもしれませんが、
図形エディターなどでは、作成された図形を再作成するためには、全ての手順を覚えておく必要があり、とても現実的ではありません。
このような場合、Memento パターンを使うとうまく処理できます。
Memento パターンでは、何の値を Memento として残すべきか、Originator に委ねられています。
Originator は、必要と思われる情報を Memento として残し、Memento から状態を復元するのです。