Mementoパターンとは
振る舞いに関するデザインパターンの一つ。オブジェクトの状態の保存・復元を実装の詳細を明かさずに行うことができる。
長所
- 内部の実装や状態知らずに(カプセル化に違反せずに)、オブジェクトの状態のスナップショットを生成できる。
- CareTakerに状態履歴の保持や復元操作についての実装を分離することで、Originatorのコードを簡素化することができる。
短所
- Mementoの大量生成により、大量のメモリが必要となる場合がある。
- 不要なMementoの廃棄には、CareTakerがOriginatorのライフサイクルを把握する必要がある。
- 言語によってはMementoの内部状態の普遍性を担保できない。
利用場面
- オブジェクトの状態復元のために、状態のスナップショットを生成したい場合。
(トランザクションやUndo機能を実装した場合等) - オブジェクトの状態の取得・変更等が適切なカプセル化に違反する場合。
(オブジェクト自体が自身のスナップショットを生成や状態の復元をするため、他オブジェクトはオブジェクトの状態等を知る必要がない)
クラス図

- Originator
自身の状態のスナップショットを作成するメソッドと、スナップショットを利用して自身の状態を復元するメソッドを宣言する。 - Concrete Originator
Originatorインタフェースで宣言されたメソッドを実装する。 - Memento
Originatorの状態復元を行うメソッドを宣言する。 - Concrete Memento
Mementoインタフェースで宣言されたメソッドを実装する。
自身を生成したOriginatorへの参照を保持し、自身のrestore()メソッドの呼び出し時に、自身が保持するOriginatorのスナップショットをOriginatorに渡し状態復元を依頼する。
Originatorのスナップショットとなるため、このオブジェクトは生成後に変更ができないように実装する必要がある。 - CareTaker
Mementoオブジェクトのリストを保持し、Originatorへの参照をもつ。Undo機能等のOriginatorオブジェクトの状態をどの時点の状態に変更するかを実装する。
実装例
Originator
package memento
type originator interface {
setState(state [3]([3]string))
getState() [3]([3]string)
backup() *memento
}
Concrete Originator
package memento
import (
"fmt"
)
// 時間や操作によって変化するような状態を保持するオブジェクト
// その状態をMementoに保存するメソッドと、状態を復元するメソッドを実装する
type Game struct {
isFirstPlayer bool
state [3]([3]string)
}
func NewGame() *Game {
row := [3]string{"-", "-", "-"}
state := [3]([3]string){row, row, row}
return &Game{isFirstPlayer: true, state: state}
}
func (g *Game) setState(state [3]([3]string)) {
g.state = state
}
func (g *Game) getState() [3]([3]string) {
return g.state
}
// 現在の状態をMementoに保存する。
func (g *Game) backup() *memento {
var game originator = g
var s memento = newSnapshot(&game)
return &s
}
func (g *Game) SetMark(row, column int) error {
if row > 3 || column > 3 {
return fmt.Errorf("invalid index")
}
if g.isFirstPlayer {
g.state[row - 1][column - 1] = "o"
} else {
g.state[row - 1][column - 1] = "x"
}
g.isFirstPlayer = !g.isFirstPlayer
return nil
}
func (g *Game) Show() {
for _, v := range g.state {
fmt.Println(v)
}
}
Memento
package memento
type memento interface {
restore()
}
Concrete Memento
package memento
type snapshot struct {
originator *originator
state [3]([3]string)
}
func newSnapshot(o *originator) *snapshot {
return &snapshot{
originator: o,
state: (*o).getState(),
}
}
// ある時点の状態でOriginatorの状態を復元する。
func (s *snapshot) restore() {
(*(s.originator)).setState(s.state)
}
Care Taker
package memento
type CareTaker struct {
originator originator
history []*memento
}
func NewCareTaker(originator originator) *CareTaker {
return &CareTaker{originator: originator}
}
func (c *CareTaker) MakeBackup() {
c.history = append(c.history, c.originator.backup())
}
func (c *CareTaker) Undo() {
if backup := c.popHistory(); backup != nil {
(*backup).restore()
}
}
func (c *CareTaker) popHistory() *memento {
if len(c.history) == 0 {
return nil
}
lastIndex := len(c.history) - 1
backup := c.history[lastIndex]
c.history = c.history[:lastIndex]
return backup
}
動作確認
package main
import (
"fmt"
"memento/memento"
)
func main() {
game := memento.NewGame()
caretaker := memento.NewCareTaker(game)
game.Show()
fmt.Println("== make backup ==")
caretaker.MakeBackup()
fmt.Println("== set mark at (1, 1) ==")
game.SetMark(1, 1)
fmt.Println("== make backup ==")
caretaker.MakeBackup()
game.Show()
fmt.Println("== set mark at (3, 2) ==")
game.SetMark(3, 2)
game.Show()
fmt.Println("== Undo ==")
caretaker.Undo()
game.Show()
fmt.Println("== Undo ==")
caretaker.Undo()
game.Show()
}
>> go run .
[- - -]
[- - -]
[- - -]
== make backup ==
== set mark at (1, 1) ==
== make backup ==
[o - -]
[- - -]
[- - -]
== set mark at (3, 2) ==
[o - -]
[- - -]
[- x -]
== Undo ==
[o - -]
[- - -]
[- - -]
== Undo ==
[- - -]
[- - -]
[- - -]
コメント