106、23种设计模式之备忘录模式(15/23)

一、定义

备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏对象封装性的前提下,捕获并外部化对象的内部状态,以便后续恢复到该状态。其核心思想是通过一个"备忘录"对象存储原发器(Originator)的状态,并由管理者(Caretaker)负责保存和传递备忘录,实现状态的撤销与恢复。

二、优缺点

1.优点:

  • 封装性保护:原发器的内部状态对外部隐藏,仅通过备忘录接口访问,避免直接暴露实现细节。
  • 撤销/重做支持:适合需要回退到历史状态的场景(如文本编辑器的Ctrl+Z)。
  • 灵活性:备忘录可存储在内存、数据库或文件中,适应不同需求。

2.缺点:

  • 内存消耗:保存大量备忘录或复杂状态时,内存占用可能显著增加。
  • 系统复杂度:简单场景下引入备忘录可能增加代码复杂度。
  • 线程安全:多线程环境下需额外处理备忘录的创建、存储和恢复的同步问题。

三、应用场景

  • 需要撤销/重做功能的系统:如Word文档编辑、图形设计软件。
  • 游戏存档与恢复:保存角色状态、关卡进度。
  • 复杂对象状态管理:避免直接暴露对象内部属性,同时支持状态回滚。
  • 多级历史记录:如浏览器后退功能,需维护多个历史状态。

四、C# 示例代码

以下是一个完整的C#实现,模拟游戏角色状态保存与恢复:

csharp 复制代码
using System;
using System.Collections.Generic;

// 备忘录类:存储角色状态
public class CharacterMemento
{
    public int Level { get; }
    public int Health { get; }
    public string CurrentWeapon { get; }
    public Position Position { get; }

    public CharacterMemento(int level, int health, string weapon, Position position)
    {
        Level = level;
        Health = health;
        CurrentWeapon = weapon;
        Position = position;
    }
}

// 位置类
public class Position
{
    public int X { get; set; }
    public int Y { get; set; }

    public Position(int x, int y)
    {
        X = x;
        Y = y;
    }
}

// 原发器类:游戏角色
public class GameCharacter
{
    public string Name { get; set; }
    public int Level { get; set; }
    public int Health { get; set; }
    public string CurrentWeapon { get; set; }
    public Position Position { get; set; }

    // 创建备忘录(存档)
    public CharacterMemento Save()
    {
        return new CharacterMemento(Level, Health, CurrentWeapon, Position);
    }

    // 从备忘录恢复(读档)
    public void Restore(CharacterMemento memento)
    {
        Level = memento.Level;
        Health = memento.Health;
        CurrentWeapon = memento.CurrentWeapon;
        Position = memento.Position;
        Console.WriteLine($"角色已恢复到: 等级{Level}, 生命{Health}, 武器{CurrentWeapon}, 位置({Position.X},{Position.Y})");
    }

    // 显示当前状态
    public void Display()
    {
        Console.WriteLine($"当前状态: {Name} 等级{Level}, 生命{Health}, 武器{CurrentWeapon}, 位置({Position.X},{Position.Y})");
    }
}

// 管理者类:保存备忘录
public class SaveGameManager
{
    private Dictionary<string, CharacterMemento> _saves = new Dictionary<string, CharacterMemento>();

    public void AddSave(string saveName, CharacterMemento memento)
    {
        _saves.Add(saveName, memento);
        Console.WriteLine($"游戏已存档: {saveName}");
    }

    public CharacterMemento GetSave(string saveName)
    {
        if (_saves.TryGetValue(saveName, out var memento))
        {
            return memento;
        }
        throw new KeyNotFoundException($"未找到存档: {saveName}");
    }
}

// 客户端代码
class Program
{
    static void Main()
    {
        // 初始化角色
        var character = new GameCharacter
        {
            Name = "勇士",
            Level = 1,
            Health = 100,
            CurrentWeapon = "铁剑",
            Position = new Position(0, 0)
        };

        // 创建管理者
        var manager = new SaveGameManager();

        // 显示初始状态
        character.Display();

        // 存档
        var memento = character.Save();
        manager.AddSave("初始存档", memento);

        // 修改状态(模拟游戏过程)
        character.Level = 5;
        character.Health = 80;
        character.CurrentWeapon = "火焰剑";
        character.Position = new Position(10, 20);
        Console.WriteLine("\n游戏进行中,角色状态变化:");
        character.Display();

        // 恢复存档
        Console.WriteLine("\n恢复初始存档:");
        character.Restore(manager.GetSave("初始存档"));
        character.Display();
    }
}

输出结果:

csharp 复制代码
当前状态: 勇士 等级1, 生命100, 武器铁剑, 位置(0,0)
游戏已存档: 初始存档

游戏进行中,角色状态变化:
当前状态: 勇士 等级5, 生命80, 武器火焰剑, 位置(10,20)

恢复初始存档:
角色已恢复到: 等级1, 生命100, 武器铁剑, 位置(0,0)
当前状态: 勇士 等级1, 生命100, 武器铁剑, 位置(0,0)

五、关键点总结

1.角色分离:

  • 原发器(Originator):负责创建和恢复备忘录。
  • 备忘录(Memento):存储状态,提供窄接口(仅原发器可访问完整数据)。
  • 管理者(Caretaker):保存备忘录,不操作其内容。

2.封装性:备忘录通过窄接口限制外部访问,确保原发器状态安全。

3.内存管理:需权衡备忘录数量与内存消耗,避免过度保存。

4.扩展性:支持多级撤销(如用栈结构存储备忘录)或持久化存储(如数据库)。

5.适用场景:优先在需要状态回滚且直接访问对象属性会破坏封装时使用。

相关推荐
AI人工智能+电脑小能手1 天前
大白话说Java设计模式-28-模板方法模式(业务实战篇)
java·设计模式·模板方法模式·spring源码·订单系统·代码复用
leeyi1 天前
两个 Agent 怎么协作:Host-Worker 模式(第91篇-E77)
设计模式·agent·ai编程
AI人工智能+电脑小能手2 天前
大白话说Java设计模式-26-策略模式(业务实战篇)
java·spring·设计模式·策略模式·支付系统·算法切换
星火10242 天前
【Groovy翻译-进阶篇】Groovy 中的设计模式
后端·设计模式·groovy
AI人工智能+电脑小能手2 天前
大白话说Java设计模式-27-策略模式(源码剖析篇)
java·spring·设计模式·策略模式·源码分析·事务管理·comparator
SL_staff2 天前
风控规则如何从硬编码解耦?我们在 JVS-Rules 中的实践与思考
java·spring boot·设计模式
小此方2 天前
Linux加餐(一):藏在Linux中的设计模式(一)策略模式与日志
linux·设计模式·策略模式
To_OC3 天前
从一行入口代码啃透 NestJS:工厂模式、装饰器和依赖注入是怎么串起来的
后端·设计模式·nestjs
东方小月3 天前
从零开发一个 Coding Agent(十二):实现版本化 JSONL 与真实 CLI 入口
前端·设计模式·前端框架
程序员贺加贝3 天前
轻制造SaaS的生产闭环建模-BOM工单领料报工质检与入库
java·设计模式·架构