Python 备忘录模式(Memento)

备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下,捕获并保存一个对象的内部状态,以便在需要时能够恢复到之前的状态。

想象一下你在玩一个游戏,游戏提供了"存档"功能。你可以随时保存当前游戏进度,如果后续游戏失败或者想重新尝试某个关卡,就可以读取之前的存档。备忘录模式就是实现这种"存档-读档"功能的编程解决方案。


为什么需要备忘录模式?

应用场景

  • 文本编辑器:实现撤销(Undo)和重做(Redo)功能
  • 游戏开发:保存游戏进度和状态
  • 图形软件:支持操作历史记录
  • 事务处理:在操作失败时回滚到之前的状态

解决的问题

  • 需要保存对象状态,但又不想暴露对象内部细节
  • 提供状态恢复机制,支持"后悔药"功能
  • 保持对象的封装性,避免状态保存逻辑与业务逻辑混杂

备忘录模式的核心组件

备忘录模式包含三个主要角色:

1. 发起人(Originator)

  • 需要保存状态的对象
  • 创建备忘录来记录当前状态
  • 使用备忘录恢复之前的状态

2. 备忘录(Memento)

  • 存储发起人对象的内部状态
  • 防止发起人之外的对象访问备忘录内容

3. 管理者(Caretaker)

  • 负责保存备忘录
  • 不能对备忘录的内容进行操作或检查


Python 实现备忘录模式

让我们通过一个文本编辑器的例子来具体实现备忘录模式。

基础实现

实例

class TextEditorMemento:
    """备忘录类 - 保存文本编辑器的状态"""
   
    def __init__(self, content):
        self._content = content
   
    def get_content(self):
        """获取保存的内容"""
        return self._content


class TextEditor:
    """发起人类 - 文本编辑器"""
   
    def __init__(self):
        self._content = ""
   
    def write(self, text):
        """写入文本"""
        self._content += text
   
    def get_content(self):
        """获取当前内容"""
        return self._content
   
    def save(self):
        """创建备忘录 - 保存当前状态"""
        return TextEditorMemento(self._content)
   
    def restore(self, memento):
        """恢复状态 - 从备忘录读取"""
        self._content = memento.get_content()


class HistoryManager:
    """管理者类 - 历史记录管理器"""
   
    def __init__(self):
        self._mementos = []
   
    def push(self, memento):
        """保存备忘录"""
        self._mementos.append(memento)
   
    def pop(self):
        """获取最新的备忘录"""
        if self._mementos:
            return self._mementos.pop()
        return None

完整示例代码

实例

# 备忘录模式完整示例
def memo_pattern_demo():
    # 创建文本编辑器和历史管理器
    editor = TextEditor()
    history = HistoryManager()
   
    print("=== 文本编辑器备忘录模式演示 ===\n")
   
    # 第一次编辑并保存
    editor.write("Hello, ")
    history.push(editor.save())
    print(f"当前内容: {editor.get_content()}")
    print("✅ 已保存状态\n")
   
    # 第二次编辑并保存
    editor.write("World!")
    history.push(editor.save())
    print(f"当前内容: {editor.get_content()}")
    print("✅ 已保存状态\n")
   
    # 第三次编辑但不保存
    editor.write(" This is new text.")
    print(f"当前内容: {editor.get_content()}")
    print("❌ 未保存当前编辑\n")
   
    # 撤销操作 - 恢复到上一次保存的状态
    memento = history.pop()
    if memento:
        editor.restore(memento)
        print(f"撤销后内容: {editor.get_content()}")
        print("↩️ 已撤销到上一个保存点\n")
   
    # 再次撤销
    memento = history.pop()
    if memento:
        editor.restore(memento)
        print(f"再次撤销后内容: {editor.get_content()}")
        print("↩️ 已撤销到最初状态")


# 运行演示
if __name__ == "__main__":
    memo_pattern_demo()

运行结果:


高级应用:支持多种状态的备忘录

在实际应用中,我们可能需要保存对象的多个属性。下面是一个更复杂的例子:

实例

import datetime
import json

class GameStateMemento:
    """游戏状态备忘录"""
   
    def __init__(self, level, score, player_health, inventory):
        self._state = {
            'level': level,
            'score': score,
            'player_health': player_health,
            'inventory': inventory.copy(),  # 创建副本避免引用问题
            'timestamp': datetime.datetime.now().isoformat()
        }
   
    def get_state(self):
        """获取完整状态"""
        return self._state.copy()
   
    def get_timestamp(self):
        """获取保存时间"""
        return self._state['timestamp']


class Game:
    """游戏类 - 发起人"""
   
    def __init__(self):
        self.level = 1
        self.score = 0
        self.player_health = 100
        self.inventory = ['sword', 'potion']
   
    def play(self, level_increase=1, score_increase=100, health_change=0, new_item=None):
        """模拟游戏进程"""
        self.level += level_increase
        self.score += score_increase
        self.player_health += health_change
       
        if new_item and new_item not in self.inventory:
            self.inventory.append(new_item)
       
        # 生命值不能为负
        self.player_health = max(0, self.player_health)
   
    def display_status(self):
        """显示当前状态"""
        status = f"""
🎮 游戏状态:
  关卡: {self.level}
  分数: {self.score}
  生命值: {self.player_health}
  背包: {', '.join(self.inventory)}
        """

        print(status)
   
    def save_game(self):
        """保存游戏状态"""
        return GameStateMemento(
            self.level,
            self.score,
            self.player_health,
            self.inventory
        )
   
    def load_game(self, memento):
        """加载游戏状态"""
        state = memento.get_state()
        self.level = state['level']
        self.score = state['score']
        self.player_health = state['player_health']
        self.inventory = state['inventory']


class SaveManager:
    """存档管理器"""
   
    def __init__(self):
        self.saves = {}
   
    def create_save(self, save_name, memento):
        """创建存档"""
        self.saves[save_name] = memento
        print(f"💾 存档 '{save_name}' 创建成功!")
   
    def load_save(self, save_name):
        """加载存档"""
        if save_name in self.saves:
            print(f"🔄 正在加载存档 '{save_name}'...")
            return self.saves[save_name]
        else:
            print(f"❌ 存档 '{save_name}' 不存在!")
            return None
   
    def list_saves(self):
        """列出所有存档"""
        if not self.saves:
            print("📁 暂无存档")
            return
       
        print("\n📋 存档列表:")
        for name, memento in self.saves.items():
            timestamp = memento.get_timestamp()
            print(f"  - {name} (保存于: {timestamp})")


# 高级示例演示
def advanced_memo_demo():
    print("=== 游戏存档系统演示 ===\n")
   
    game = Game()
    save_manager = SaveManager()
   
    # 初始状态
    print("🎯 开始新游戏:")
    game.display_status()
   
    # 游戏进程
    print("🚀 进行游戏...")
    game.play(level_increase=2, score_increase=500, new_item='shield')
    game.display_status()
   
    # 保存第一个存档
    save_manager.create_save("第一关通关", game.save_game())
   
    # 继续游戏
    print("⚔️ 继续冒险...")
    game.play(level_increase=1, score_increase=200, health_change=-30, new_item='magic_wand')
    game.display_status()
   
    # 保存第二个存档
    save_manager.create_save("第二关开始", game.save_game())
   
    # 显示存档列表
    save_manager.list_saves()
   
    # 加载第一个存档
    print("\n⏪ 回到第一关通关状态:")
    first_save = save_manager.load_save("第一关通关")
    if first_save:
        game.load_game(first_save)
        game.display_status()


# 运行高级示例
if __name__ == "__main__":
    advanced_memo_demo()

备忘录模式的优缺点

优点

  1. 封装性保护:不暴露对象内部状态,保持封装性
  2. 简化发起人:将状态保存逻辑从业务逻辑中分离
  3. 易于实现恢复:提供简单的状态恢复机制
  4. 支持多次撤销:可以保存多个时间点的状态

缺点

  1. 内存消耗:如果状态很大或保存频繁,会消耗大量内存
  2. 管理者开销:需要额外的类来管理备忘录
  3. 生命周期管理:需要妥善处理备忘录的创建和销毁

最佳实践和注意事项

1. 内存优化策略

实例

# 限制保存的历史记录数量
class LimitedHistoryManager:
    def __init__(self, max_size=10):
        self._mementos = []
        self._max_size = max_size
   
    def push(self, memento):
        if len(self._mementos) >= self._max_size:
            # 移除最旧的记录
            self._mementos.pop(0)
        self._mementos.append(memento)

2. 处理复杂对象状态

对于包含复杂引用的对象,需要确保备忘录创建的是深拷贝:

实例

import copy

class ComplexMemento:
    def __init__(self, complex_state):
        # 使用深拷贝避免引用问题
        self._state = copy.deepcopy(complex_state)

3. 选择性保存

不是所有状态都需要保存,可以选择只保存重要的属性:

实例

def create_selective_memento(self):
    """选择性保存重要状态"""
    important_state = {
        'essential_data': self.essential_data,
        'critical_settings': self.critical_settings
        # 忽略临时数据和缓存
    }
    return SelectiveMemento(important_state)

实际应用场景

1. 图形编辑器

实例

class GraphicEditor:
    def save_state(self):
        return GraphicMemento(
            self.selected_shapes.copy(),
            self.canvas_state,
            self.view_settings
        )

2. 配置管理器

实例

class ConfigManager:
    def backup_config(self):
        return ConfigMemento(
            self.current_settings.copy(),
            self.user_preferences
        )

3. 事务操作

实例

class DatabaseTransaction:
    def create_checkpoint(self):
        return TransactionMemento(
            self.connection_state,
            self.pending_operations.copy()
        )

总结

备忘录模式是一个强大而实用的设计模式,它为我们提供了一种优雅的状态保存和恢复机制。通过将状态保存逻辑与业务逻辑分离,它不仅保持了代码的整洁性,还增强了系统的可维护性。

关键要点:

  • 备忘录模式的核心是"保存-恢复"机制
  • 三个核心角色各司其职,职责分明
  • 在实际使用中要注意内存管理和状态选择
  • 适用于需要撤销、重做或状态回滚的场景