はじめに
Java で Memento パターンを実装をしてみました.
Memento パターンとは
オブジェクトの状態を保存しておき, 元に戻せるようにしておく. オブジェクトを以前の状態に (ロールバックにより) 戻す能力を提供する.
パターン適用前
以前, State Pattern を実装したコードを改造. ステートを保存できるようにします.
public class StatePatternMemento {
public static void main (String[] args) {
Context context = new Context (new Morning ());
while (!context.isDone ()) {
context.greeting ();
}
}
}
class Context {
private State state;
public Context (State state) {
this.state = state;
}
public void greeting () {
state = state.greeting ();
}
public boolean isDone () {
return state == null;
}
}
interface State {
public State greeting ();
}
class Morning implements State {
public Morning () {
System.out.println (" === It's 09:00 ===");
}
public State greeting () {
System.out.println ("Good morning!! ('▽`)");
return new Afternoon ();
}
}
class Afternoon implements State {
public Afternoon () {
System.out.println (" === It's 15:00 ===");
}
public State greeting () {
System.out.println ("Good afternoon!! ('▽`)");
return new Evening ();
}
}
class Evening implements State {
public Evening () {
System.out.println (" === It's 20:00 ===");
}
public State greeting () {
System.out.println ("Good evening!! ('▽`)");
return null;
}
}
パターン適用後
状態保存する機能を順番に追加していく.
メメントパターンは, 保存/ 復元される側とする側の役割を独立させるのが普通.
- originator (代表者) 内部属性を保存されるもの.
- caretaker (世話役) originator 内部属性 (状態) を保存するコンテナ.
- Memento (記念品) originator 内部属性 (状態) を記憶するデータ構造.
その 1 初期状態を復元
まずは, 初期状態を追加. originator である Context を拡張する.
class Context {
private State initialState;
public Context (State state) {
this.state = state;
this.initialState = state;
}
public void restoreState () {
System.out.println ();
System.out.println (" === Restore State ===");
System.out.println ();
state = initialState;
}
}
初期状態を保存する属性と 外部から restore するためのインタフェースを用意.
public class StatePatternMemento1 {
public static void main (String[] args) {
Context context = new Context (new Morning ());
while (!context.isDone ()) {
context.greeting ();
}
context.restoreState ();
while (!context.isDone ()) {
context.greeting ();
}
}
}
class Context {
private State state;
private State initialState;
public Context (State state) {
this.state = state;
this.initialState = state;
}
public void greeting () {
state = state.greeting ();
}
public boolean isDone () {
return state == null;
}
public void restoreState () {
System.out.println ();
System.out.println (" === Restore State ===");
System.out.println ();
state = initialState;
}
}
interface State {
public State greeting ();
}
class Morning implements State {
public Morning () {
System.out.println (" === It's 09:00 ===");
}
public State greeting () {
System.out.println ("Good morning!! ('▽`)");
return new Afternoon ();
}
}
class Afternoon implements State {
public Afternoon () {
System.out.println (" === It's 15:00 ===");
}
public State greeting () {
System.out.println ("Good afternoon!! ('▽`)");
return new Evening ();
}
}
class Evening implements State {
public Evening () {
System.out.println (" === It's 20:00 ===");
}
public State greeting () {
System.out.println ("Good evening!! ('▽`)");
return null;
}
}
実行結果
=== It's 09:00 ===
Good morning!! ('▽`)
=== It's 15:00 ===
Good afternoon!! ('▽`)
=== It's 20:00 ===
Good evening!! ('▽`)
=== Restore State ===
Good morning!! ('▽`)
=== It's 15:00 ===
Good afternoon!! ('▽`)
=== It's 20:00 ===
Good evening!! ('▽`)
その 2 状態 + アルファを復元
状態 + アルファを保存するためのデータ構造を用意する. これこそが memento!!
ここでは, アルファは count とする.
private int count;
count と state を保持するデータ構造 memento クラスを作成. memento のコンストラクタで状態を保存する. memento の getter で属性を取得してリストアする.
private static class Memento {
private State state;
private int count;
public Memento (State stateToSave, int countToSave) {
state = stateToSave;
count = countToSave;
}
public State getSavedState () { return state; }
public int getSavedCount () { return count; }
}
全コード
public class StatePatternMemento2 {
public static void main (String[] args) {
Context context = new Context (new Morning ());
while (!context.isDone ()) {
context.greeting ();
}
context.restoreState ();
while (!context.isDone ()) {
context.greeting ();
}
}
}
class Context {
private State state;
private int count;
private Memento m;
public Context (State state) {
this.state = state;
}
public void greeting () {
state = state.greeting ();
count++;
if (count == 1) {
m = new Memento (state, count);
}
}
public boolean isDone () {
return state == null;
}
public void restoreState () {
System.out.println ();
System.out.println (" === Restore State ===");
System.out.println ();
state = m.getSavedState ();
count = m.getSavedCount ();
}
private static class Memento {
private State state;
private int count;
public Memento (State stateToSave, int countToSave) {
state = stateToSave;
count = countToSave;
}
public State getSavedState () { return state; }
public int getSavedCount () { return count; }
}
}
interface State {
public State greeting ();
}
class Morning implements State {
public Morning () {
System.out.println (" === It's 09:00 ===");
}
public State greeting () {
System.out.println ("Good morning!! ('▽`)");
return new Afternoon ();
}
}
class Afternoon implements State {
public Afternoon () {
System.out.println (" === It's 15:00 ===");
}
public State greeting () {
System.out.println ("Good afternoon!! ('▽`)");
return new Evening ();
}
}
class Evening implements State {
public Evening () {
System.out.println (" === It's 20:00 ===");
}
public State greeting () {
System.out.println ("Good evening!! ('▽`)");
return null;
}
}
実行結果
=== It's 09:00 ===
Good morning!! ('▽`)
=== It's 15:00 ===
Good afternoon!! ('▽`)
=== It's 20:00 ===
Good evening!! ('▽`)
=== Restore State ===
Good afternoon!! ('▽`)
=== It's 20:00 ===
Good evening!! ('▽`)
その 3 スナップショットを復元
最後にスナップショット機能を追加. 保存するためには, 第三者を登場させて外部に状態を保持する.
class SnapShot {
private List<Object> states = new ArrayList<Object>();
public void addMemento (Object m) { states.add (m); }
public Object getMemento (int index) { return states.get (index); }
}
ここでのポイントは, Context の情報のみをもつ Memento データを 保存しておくこと. 保存する情報はメモリ上で小さくすることが目的.
全コード
import java.util.List;
import java.util.ArrayList;
public class StatePatternMemento3 {
public static void main (String[] args) {
Context context = new Context (new Morning ());
SnapShot snaps = new SnapShot ();
snaps.addMemento (context.saveToMemento ());
while (!context.isDone ()) {
context.greeting ();
}
context.restoreFromMemento (snaps.getMemento (0));
while (!context.isDone ()) {
context.greeting ();
}
}
}
class Context {
private State state;
private int count;
public Context (State state) {
this.state = state;
}
public void greeting () {
state = state.greeting ();
count++;
}
public boolean isDone () {
return state == null;
}
public Object saveToMemento () {
System.out.println (" == Save to Memento == ");
return new Memento (state, count);
}
public void restoreFromMemento (Object m) {
if (m instanceof Memento) {
Memento memento = (Memento) m;
state = memento.getSavedState ();
count = memento.getSavedCount ();
System.out.println ();
System.out.println (" === Restore State ===");
System.out.println ();
}
}
private static class Memento {
private State state;
private int count;
public Memento (State stateToSave, int countToSave) {
state = stateToSave;
count = countToSave;
}
public State getSavedState () { return state; }
public int getSavedCount () { return count; }
}
}
class SnapShot {
private List<Object> states = new ArrayList<Object>();
public void addMemento (Object m) { states.add (m); }
public Object getMemento (int index) { return states.get (index); }
}
interface State {
public State greeting ();
}
class Morning implements State {
public Morning () {
System.out.println (" === It's 09:00 ===");
}
public State greeting () {
System.out.println ("Good morning!! ('▽`)");
return new Afternoon ();
}
}
class Afternoon implements State {
public Afternoon () {
System.out.println (" === It's 15:00 ===");
}
public State greeting () {
System.out.println ("Good afternoon!! ('▽`)");
return new Evening ();
}
}
class Evening implements State {
public Evening () {
System.out.println (" === It's 20:00 ===");
}
public State greeting () {
System.out.println ("Good evening!! ('▽`)");
return null;
}
}
実行結果
=== It's 09:00 ===
Good morning!! ('▽`)
=== It's 15:00 ===
Good afternoon!! ('▽`)
=== It's 20:00 ===
Good evening!! ('▽`)
=== Restore State ===
Good morning!! ('▽`)
=== It's 15:00 ===
Good afternoon!! ('▽`)
=== It's 20:00 ===
Good evening!! ('▽`)