Skip to content

备忘录模式

引言

在软件系统中,当需要记录对象状态变化(如文档编辑的历史记录)或支持状态回滚(如事务操作撤销)时,直接操作状态会导致逻辑耦合历史管理混乱。备忘录模式通过封装状态快照分离状态管理,实现"时光机"般的状态回溯能力,成为状态管理的优雅解决方案。

诞生背景

GoF在《设计模式》中提出备忘录模式,解决三大痛点:

  • 状态暴露:对象内部状态直接暴露破坏封装性
  • 历史管理复杂:客户端需自行管理状态历史记录
  • 回滚耦合:状态恢复逻辑与业务代码高度耦合

演进过程

  • GoF基础(1994):确立三核心角色(发起人、备忘录、管理者)
  • 现代应用扩展:数据库事务日志、浏览器历史记录管理
  • 跨领域应用:游戏存档系统、分布式系统状态快照

核心概念

  • 发起人(Originator)
    • 创建备忘录保存自身状态
    • 从备忘录恢复历史状态
  • 备忘录(Memento)
    • 存储发起人对象的内部状态快照
  • 管理者(Caretaker)
    • 负责保存和管理备忘录对象

通用实现

Java 实现

java
// 备忘录:存储编辑器状态
class EditorMemento {
    private final String content;
    
    public EditorMemento(String content) {
        this.content = content;
    }
    
    public String getContent() {
        return content;
    }
}

// 发起人:文本编辑器
class TextEditor {
    private String content;
    
    public void write(String text) {
        content = text;
    }
    
    public EditorMemento save() {
        return new EditorMemento(content);
    }
    
    public void restore(EditorMemento memento) {
        content = memento.getContent();
    }
    
    public void print() {
        System.out.println("Current content: " + content);
    }
}

// 管理者:历史记录管理
class HistoryManager {
    private final Stack<EditorMemento> history = new Stack<>();
    
    public void saveState(EditorMemento memento) {
        history.push(memento);
    }
    
    public EditorMemento undo() {
        if (!history.isEmpty()) {
            return history.pop();
        }
        return null;
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        HistoryManager history = new HistoryManager();
        
        editor.write("First draft");
        history.saveState(editor.save());
        editor.write("Second version");
        
        editor.restore(history.undo()); // 恢复到第一版
        editor.print(); // 输出: Current content: First draft
    }
}

PHP 实现

php
// 备忘录:存储购物车状态
class CartMemento {
    private array $items;
    
    public function __construct(array $items) {
        $this->items = $items;
    }
    
    public function getItems(): array {
        return $this->items;
    }
}

// 发起人:购物车
class ShoppingCart {
    private array $items = [];
    
    public function addItem(string $item): void {
        $this->items[] = $item;
    }
    
    public function save(): CartMemento {
        return new CartMemento($this->items);
    }
    
    public function restore(CartMemento $memento): void {
        $this->items = $memento->getItems();
    }
    
    public function showItems(): void {
        echo "Cart items: " . implode(', ', $this->items) . "\n";
    }
}

// 管理者:状态管理
class CartHistory {
    private array $history = [];
    
    public function saveState(CartMemento $memento): void {
        $this->history[] = $memento;
    }
    
    public function undo(): ?CartMemento {
        return array_pop($this->history);
    }
}

// 客户端使用
$cart = new ShoppingCart();
$history = new CartHistory();

$cart->addItem("Laptop");
$history->saveState($cart->save());

$cart->addItem("Phone");
if ($previousState = $history->undo()) {
    $cart->restore($previousState);
}

$cart->showItems(); // 输出: Cart items: Laptop

应用场景

  • 撤销/重做功能:文本编辑器、图形设计软件
  • 事务回滚:数据库操作中的状态恢复
  • 游戏存档:保存和恢复游戏进度
  • 配置管理:系统配置的历史版本管理
  • 工作流状态:业务流程的状态快照

案例:图形编辑器

Java 实现

java
// 备忘录:存储图形状态
class GraphicMemento {
    private final String color;
    private final int size;
    
    public GraphicMemento(String color, int size) {
        this.color = color;
        this.size = size;
    }
    
    public String getColor() { return color; }
    public int getSize() { return size; }
}

// 发起人:图形对象
class Graphic {
    private String color;
    private int size;
    
    public void setProperties(String color, int size) {
        this.color = color;
        this.size = size;
    }
    
    public GraphicMemento save() {
        return new GraphicMemento(color, size);
    }
    
    public void restore(GraphicMemento memento) {
        this.color = memento.getColor();
        this.size = memento.getSize();
    }
    
    public void describe() {
        System.out.printf("Color: %s, Size: %dpx\n", color, size);
    }
}

// 客户端使用
public class GraphicEditor {
    public static void main(String[] args) {
        Graphic circle = new Graphic();
        Stack<GraphicMemento> history = new Stack<>();
        
        circle.setProperties("Red", 100);
        history.push(circle.save());
        
        circle.setProperties("Blue", 150); // 修改属性
        circle.restore(history.pop()); // 撤销修改
        
        circle.describe(); // 输出: Color: Red, Size: 100px
    }
}

PHP 实现

php
// 备忘录:存储用户设置
class UserSettingsMemento {
    private string $theme;
    private int $fontSize;
    
    public function __construct(string $theme, int $fontSize) {
        $this->theme = $theme;
        $this->fontSize = $fontSize;
    }
    
    public function getTheme(): string {
        return $this->theme;
    }
    
    public function getFontSize(): int {
        return $this->fontSize;
    }
}

// 发起人:用户设置
class UserSettings {
    private string $theme = 'light';
    private int $fontSize = 12;
    
    public function changeSettings(string $theme, int $fontSize): void {
        $this->theme = $theme;
        $this->fontSize = $fontSize;
    }
    
    public function save(): UserSettingsMemento {
        return new UserSettingsMemento($this->theme, $this->fontSize);
    }
    
    public function restore(UserSettingsMemento $memento): void {
        $this->theme = $memento->getTheme();
        $this->fontSize = $memento->getFontSize();
    }
    
    public function display(): void {
        echo "Theme: {$this->theme}, Font Size: {$this->fontSize}px\n";
    }
}

// 客户端使用
$settings = new UserSettings();
$history = [];

$settings->changeSettings('dark', 14);
$history[] = $settings->save(); // 保存状态1

$settings->changeSettings('blue', 16); // 修改设置
$settings->restore(end($history)); // 恢复到状态1

$settings->display(); // 输出: Theme: dark, Font Size: 14px

优点

  • 状态封装:不暴露对象实现细节
  • 简化发起人:移除状态管理职责
  • 时间点恢复:支持任意历史状态恢复
  • 职责分离:状态管理独立于业务逻辑

缺点

  • 内存消耗:大量状态快照占用内存
  • 性能开销:频繁保存状态影响性能
  • 复杂克隆:深拷贝复杂对象可能困难
  • 状态暴露风险:管理者可能误操作备忘录内部状态

扩展

  • 增量备忘录
    • 只存储状态变化部分而非完整对象
  • 持久化备忘录
    • 将状态快照保存到数据库或文件系统
  • 多级撤销栈
    • 实现无限级撤销/重做功能
  • 状态压缩
    • 定期清理不必要的历史状态

模式协作

  • 与命令模式:实现可撤销操作(命令保存执行前的备忘录)
  • 与原型模式:使用原型克隆创建备忘录
  • 与责任链模式:多级状态恢复(如不同版本的状态管理)
  • 与迭代器模式:遍历历史状态集合

延伸思考

  • 数据库事务:事务日志本质是备忘录模式的工业级实现
  • 版本控制系统:Git等工具的核心是备忘录模式的扩展应用
  • 前端状态管理:Redux中的时间旅行调试基于备忘录原理
  • 内存优化策略:使用差异存储(只保存变化部分)减少内存占用
  • 快照安全:敏感数据在备忘录中的加密处理

总结

备忘录模式是对象状态的时间胶囊,通过封装状态快照与分离状态管理,实现状态保存与恢复的优雅解耦。其核心价值在于:状态封装的保护机制历史管理的时空隧道。在需要撤销操作、状态回溯或事务管理的场景中,备忘录模式能显著提升系统的灵活性和可靠性,成为软件设计中不可或缺的"时光机器"。