Mementoパターン
概要
オブジェクトの状態を保存しておき、後から戻せるようにするためのパターン
Memento(保存するデータそのもの)、CareTaker(保存先)、Originator(状態の主)に分けることで結合度を下げることができる
使い方
bad
class Form {
private name = ''
private tel = ''
private history: { name: string; tel: string }[] = []
inputName(name: string) {
this.name = name
this.history.push({
name: this.name,
tel: this.tel
})
}
inputTel(tel: string) {
this.tel = tel
this.history.push({
name: this.name,
tel: this.tel
})
}
getName() {
return this.name
}
getTel() {
return this.tel
}
undo() {
this.history.pop();
const latest = this.history[this.history.length - 1]
this.name = latest.name
this.tel = latest.tel
}
}
Form自身が以下の責務を全部持ってしまっている
name,telの状態を管理name,telの状態を変更name,telの参照- 過去の状態を保存
- 過去の状態の保持
- 過去の状態を復元
good
// Memento: 残すデータそのもの(不変)
class FormMemento {
constructor(private readonly name: string, private readonly tel: string) {}
getName() {
return this.name
}
getTel() {
return this.tel
}
}
// Originator: 状態の主
class FormOriginator {
private name = ''
private tel = ''
inputName(name: string) {
this.name = name
}
inputTel(tel: string) {
this.tel = tel
}
// Mementoに状態を保存する
save(): FormMemento {
return new FormMemento(this.name, this.tel)
}
// Mementoから状態を復元する
restore(memento: FormMemento): void {
this.name = memento.getName();
this.tel = memento.getTel();
}
}
// CareTaker: Mementoを保管、管理するもの
class FormCareTaker {
private history: FormMemento[] = []
push(memento: FormMemento) {
this.history.push(memento)
}
pop() {
return this.memento.pop()
}
}
const form = new FormOriginator();
const history = new FormCareTaker();
form.inputName = 'taro'
form.inputTel = '09001234567'
history.push(form.save())
form.restore(history.pop())
Formに全部詰め込まれていた責務を以下のように分離
Memento: 状態のスナップショットを保持Originator: 状態を保持(name,telを保持、管理)CareTaker: 状態のスナップショットの履歴を保持
Formの構造が変化したときにも変更するべきところが限られるので変更が強い。
また、状態も切り離されているので、状態の保持方法を柔軟に変えやすい
使い所
元に戻す処理が発生するとき- 何かを編集
- Transaction的な処理