簡介
備忘錄模式(Memento Pattern)是一種行為型設計模式,它允許生成對象狀態的快照並在以後將其還原。備忘錄模式不會影響它所處理對象的內部結構,也不會影響快照中存儲的數據。簡單來説,它就像遊戲中的“保存”和“加載”功能。
組成角色:
- Originator(發起人): 主要用於生成自身狀態的快照,在需要時可以通過快照恢復自身狀態。
- Memento(備忘錄): 一個對象,用於存儲Originator在某個特定時間點的內部狀態。備忘錄對Originator之外的對象是不可見的,這樣可以保護Originator的狀態不被外部修改。通常將備忘錄類設置為不可變的類,並且通過構造函數一次性傳遞數據。
- Caretaker(管理者): 負責保存和管理備忘錄對象,但不檢查備忘錄的內容,只知道何時和為何捕捉Originator的狀態,以及何時恢復Originator的狀態。
常見使用場景
- 撤銷/重做功能: 許多應用程序(如文本編輯器、圖形編輯器)都提供了撤銷和重做操作。備忘錄模式可以用來保存每次操作之前的狀態,以便進行撤銷。
- 事務管理: 在某些事務處理系統中,可能需要在事務失敗時回滾到之前的狀態。備忘錄模式可以用來保存事務開始前的狀態。
- 程序狀態的快照和恢復: 有時需要定期保存程序的狀態,以便在出現問題時能夠恢復到最近的穩定狀態。
- 遊戲中的保存和加載: 遊戲通常需要保存玩家的進度,並在需要時加載。備忘錄模式非常適合這種場景。
優點
- 封裝性好: Originator的狀態被封裝在Memento中,對外部不可見,保證了狀態的安全性。
- 簡化了Originator: Originator只需要負責創建和恢復備忘錄,而不需要關心備忘錄的管理。備忘錄的管理交由Caretaker負責。
- 支持歷史版本: 可以保存Originator的多個歷史狀態,方便進行多次撤銷或恢復。
缺點
- 內存消耗: 如果需要保存的狀態很多或者狀態很大,可能會消耗大量的內存。
- 管理複雜性: Caretaker需要管理多個備忘錄對象,可能會增加管理的複雜性。
- 狀態恢復的性能開銷: 恢復狀態可能涉及到複雜的數據複製操作,可能會有一定的性能開銷。
示例代碼
Simple
Go
package memento
type Memento struct {
state int
}
func NewMemento(value int) *Memento {
return &Memento{
state: value,
}
}
type Originator struct {
value int
}
func NewOriginator(value int) *Originator {
return &Originator{
value: value,
}
}
func (o *Originator) TenTimes() {
o.value *= 10
}
func (o *Originator) HalfTimes() {
o.value /= 2
}
func (o *Originator) Value() int {
return o.value
}
func (o *Originator) CreateMemento() *Memento {
return NewMemento(o.value)
}
func (o *Originator) RestoreMemento(m *Memento) {
o.value = m.state
}
type Caretaker struct {
Mementos []*Memento
}
func (c *Caretaker) AddMemento(m *Memento) {
c.Mementos = append(c.Mementos, m)
}
func (c *Caretaker) GetMemento(index int) *Memento {
return c.Mementos[index]
}
客户端
package memento
import (
"fmt"
"testing"
)
func TestSimple(t *testing.T) {
caretaker := &Caretaker{
Mementos: make([]*Memento, 0),
}
n := NewOriginator(10)
caretaker.AddMemento(n.CreateMemento())
n.TenTimes()
fmt.Printf("Current value: %d\n", n.Value())
caretaker.AddMemento(n.CreateMemento())
n.TenTimes()
fmt.Printf("Current value: %d\n", n.Value())
n.RestoreMemento(caretaker.GetMemento(0))
fmt.Printf("Current value after restore: %d\n", n.Value())
}
執行輸出
Current value: 100
Current value: 1000
Current value after restore: 10
Python
from typing import List
class Memento:
"""
備忘錄類
"""
def __init__(self, value: int):
self._value = value
@property
def value(self) -> int:
"""用 property 實現屬性只讀"""
return self._value
class Originator:
"""
發起人類
"""
def __init__(self, value: int):
self._value = value
def ten_times(self):
self._value *= 10
def half_times(self):
self._value /= 2
def get_value(self) -> int:
return self._value
def create_memento(self) -> Memento:
return Memento(self._value)
def restore_memento(self, memento: Memento):
self._value = memento.value
class Caretaker:
"""負責人類"""
def __init__(self):
self._mems: List[Memento] = []
def add_memento(self, memento: Memento):
self._mems.append(memento)
def get_memento(self, index: int) -> Memento:
return self._mems[index]
if __name__ == "__main__":
caretaker = Caretaker()
n = Originator(10)
caretaker.add_memento(n.create_memento())
n.ten_times()
print(f"current value: {n.get_value()}")
caretaker.add_memento(n.create_memento())
n.ten_times()
print(f"current value: {n.get_value()}")
n.restore_memento(caretaker.get_memento(0))
print(f"current value after restore: {n.get_value()}")
執行輸出
current value: 100
current value: 1000
current value after restore: 10