ねらい
- カプセル化を違反することなく、オブジェクトの内部状態を外部に書き出し、後で再度読み込む
AKA
- Token
モチベーション
-
オブジェクトの内部状態を保存したいことがある
- undo/redoのサポート等
-
状態のgetter/setterを安易に公開するのはカプセル化をブチ壊す行為
- 全publicと大して変わらない
- カプセル化を守りつつ、内部状態を外部に書き出し、後で再度読み込みたい
つかいどころ
-
オブジェクトの内部状態(の一部)のスナップショットを保存する必要がある
-
あとでその状態に戻りたい
- undo/redoとか
-
- 内部状態について、直接的にgetter/setterを定義するとカプセル化をブチ壊してしまう
登場人物
-
Memento
originator
の内部状態を保持-
基本的にDTO
- こいつ自身バリバリ仕事するクラスではない
-
Originator
memento
オブジェクトを生成し、内部状態のスナップショットをとるmemento
オブジェクトを読み込み、内部状態を復元する
-
Caretaker
memento
オブジェクトを保持する人-
memento
の内容には触れない- getもsetもダメ
クライアントコードからの利用
caretaker
はoriginator
にmemento
の生成をリクエストするcaretaker
はmemento
を保持するoriginator
の内部状態を復元する必要がでてきたら、caretaker
はmemento
をoriginator
に渡すoriginator
は、memento
から値を読み込み、内部状態を復元するmemento
は使われないこともあるmemento
の後始末の責務はcaretaker
にある
結果
- カプセル化が守られる
-
Originator
がシンプルになる-
Memento
以外のパターンとしては、内部状態のバージョン管理等がある- 複雑
-
-
高コストかも
originator
の内部状態が巨大な場合、そのcloningは時間・空間ともにコスト高い- 差分のみ保存することで回避できる
-
狭い/広いインタフェースの定義
Originator
からMemento
の内容には書き込み・読み出しともに可能- 他クラスから
Memento
の内容には一切触れられない
-
隠れたコスト(空間、管理)
Caretaker
からはmemento
の中身が見えないため、Caretaker
のストレージコストが思ってたよりもかさみました、ということもOriginator
がnewして返したmemento
は、Caretaker
が責任をもってdeleteする
実装にあたり考えるべきこと
-
言語のサポート
-
狭い/広いインタフェースの定義には、C++ならfriendが使える
-
Originator
だけに限定で公開する、広いインタフェース- private/protected
Originator
とMemento
とをfriendにする
-
Originator
以外にも公開する、狭いインタフェース- public
-
-
-
内部状態の差分を保存する
- ストレージコストの削減
関連するパターン
-
Command
- undoのサポートで併用
-
Iterator
-
GoFのIteratorパターン
- Iteratorに走査の責務をもたせ、「現在の要素」といった状態を保持させる
-
そうじゃない、
Memento
ベースのパターン- Aggregateに走査の責務をもたせる
- 「走査の状態」として
Memento
クラスを定義する aggregate->next(iterationStatePtr);
という感じに使用する- 「走査の状態」を外出しするので、同時に複数の走査を行うことができる
-