GoF本 Memento
ねらい
- カプセル化を違反することなく、オブジェクトの内部状態を外部に書き出し、後で再度読み込む
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
- 狭い/広いインタフェースの定義には、C++ならfriendが使える
- 内部状態の差分を保存する
- ストレージコストの削減