Memento パターン

memento パターンはソフトウェアのデザインパターンの一つで、オブジェクトを以前の状態に戻す能力を提供する。

memento パターンは二つのオブジェクトによって用いられる。'originator'と'caretaker' である。'originator' は内部状態を持つオブジェクトである。caretaker は originator に何らかの操作を行うが、その操作の結果を元に戻す機能を持たせたいとする。まず caretaker は操作を行う前に、originator へ現状の memento を要求して得る。その後、任意の操作を行う。操作前の状態に復元するには、先程 得た memento を originator に渡す。memento 自体は、不透明オブジェクト である。このパターンを使う場合、originator が他のオブジェクトやリソースを変更してしまうかどうか注意が要る。memento パターンは単一のオブジェクトに対して働くためである。

memento パターンの古典的な例として、擬似乱数発生器や、有限オートマトンの状態などがある。

1. Memento パターンの例
-----------------

1.1. Java
---------

以下の Java プログラムは、 Memento パターンを "undo" に使った場合を示す。

import java.util.*;

class Originator { private String state; /* メモリを消費する多数の private のデータで状態に関係しないものは保存されるべきでない。

memento は小さなオブジェクトであるべき */ public void set { System.out.println; this.state = state; }

public Object saveToMemento { System.out.println; return new Memento; } public void restoreFromMemento { if  { Memento memento = m; state = memento.getSavedState; System.out.println; } }

private static class Memento { private String state;

public Memento { state = stateToSave; } public String getSavedState { return state; } } }

class Caretaker { private List savedStates = new ArrayList;

public void addMemento { savedStates.add; } public Object getMemento { return savedStates.get; } }

class MementoExample { public static void main { Caretaker caretaker = new Caretaker;

Originator originator = new Originator; originator.set; originator.set; caretaker.addMemento ); originator.set; caretaker.addMemento ); originator.set;

originator.restoreFromMemento ); } }<WtXmlEndTag />

出力: Originator: Setting state to State1 Originator: Setting state to State2 Originator: Saving to Memento. Originator: Setting state to State3 Originator: Saving to Memento. Originator: Setting state to State4 Originator: State after restoring from Memento: State3

1.2. Ruby
---------

以下の Ruby プログラムで同じパターンを示す。

!/usr/bin/env ruby -KUrequire 'rubygems' require 'spec'

class Originator class Memento def initialize

Originator の元の状態を壊しても、この Memento オブジェクトが初回以降の restore で
破壊されないよう dup が必要@state = state.dup end

def state

Originator の元の状態を壊しても、この Memento オブジェクトが二度目の restore で
破壊されないよう dup が必要@state.dup end end

attr_accessor :state

メモリに巨大なデータが多数あり、state から再構成可能であるように見せるdef save_to_memento Memento.new end

def restore_from_memento @state = m.state end

end

class Caretaker < Array; end

describe Originator do before do @caretaker = Caretaker.new @originator = Originator.new

@originator.state = "State1" end

it "should have original state" do @originator.state.should == 'State1' end

it "should update state" do @originator.state = "State2" @originator.state.should == 'State2' end

it "should save memento" do @caretaker << @originator.save_to_memento @caretaker.size.should == 1 end

it "should update state after save to memento" do @originator.state = "State3" @originator.state.should == 'State3' end

it "should save to memento again" do @caretaker << @originator.save_to_memento @caretaker.size.should == 2 end

it "should update state after save to memento again" do @originator.state = "State4"; @originator.state.should == 'State4' end

it "should restore to original save point" do @originator.restore_from_memento @caretaker[0] @originator.state.should == 'State2' end

it "should restore to second save point" do @originator.restore_from_memento @caretaker[1] @originator.state.should == 'State3' end

it "should restore after pathological munging of restored state" do @originator.state[-1] = '5' @originator.state.should == 'State5' @originator.restore_from_memento @caretaker[1] @originator.state.should == 'State3' end

it "should restore after pathological munging of original state" do @originator.state = "State6" @originator.state.should == 'State6' @caretaker << @originator.save_to_memento @originator.state[-1] = '7' @originator.state.should == 'State7' @originator.restore_from_memento @caretaker[2] @originator.state.should == 'State6' end end<WtXmlEndTag />

2. 関連項目
-------

Facade パターン
永続データ構造

3. 外部リンク
--------

[1] by Matthew Heaney
[2] with C# and .NET code samples