【从零入门23种设计模式18】行为型之备忘录模式

一、备忘录模式核心定义

备忘录模式是行为型设计模式的一种,核心目的是:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便后续需要时能将该对象恢复到原先保存的状态。

简单来说:给对象拍 "快照",并将快照保存到外部,需要时可通过快照恢复对象状态

核心解决的问题
  1. 状态备份与恢复 :无需暴露对象内部结构,即可保存 / 恢复对象状态(如编辑器撤销、游戏存档);
  2. 撤销 / 重做 :通过保存多个历史快照,实现多步撤销、重做操作;
  3. 数据隔离 :快照(备忘录)与原对象解耦,原对象修改不影响已保存的快照;
  4. 事务回滚 :保存操作前的状态,操作失败时恢复到原状态(如数据库事务、支付回滚)。
生活类比
  • 场景 1 :文档编辑器的撤销功能
    • 原发器(Originator):文档对象(包含内容、格式、光标位置等状态);
    • 备忘录(Memento):文档的快照(保存某一时刻的所有状态);
    • 负责人(Caretaker):撤销栈(管理所有快照,负责保存 / 获取快照);
    • 核心:编辑文档时,每一步操作都保存快照到撤销栈,点击 "撤销" 时从栈中取出快照恢复文档。
  • 场景 2 :游戏存档
    • 原发器:游戏角色(等级、血量、装备、位置等状态);
    • 备忘录:游戏存档文件(保存角色的所有状态);
    • 负责人:存档管理器(管理多个存档,如自动存档、手动存档);
    • 核心:玩家存档时,角色生成快照并保存为文件,读档时从文件恢复角色状态。
  • 场景 3 :数据库事务
    • 原发器:数据库表(数据记录状态);
    • 备忘录:事务日志(保存操作前的数据状态);
    • 负责人:事务管理器(管理日志,事务失败时恢复数据);
    • 核心:执行更新操作前保存数据快照,事务回滚时通过快照恢复数据。
标准角色
角色 职责 类比(编辑器场景) 代码定位
原发器(Originator) 生成备忘录(保存当前状态)、从备忘录恢复状态,是需要保存状态的对象 文档对象(生成 / 恢复快照) 业务核心对象(如DocumentGameRole
备忘录(Memento) 存储原发器的内部状态,对原发器暴露所有状态,对其他对象仅暴露有限接口 文档快照(保存内容 / 格式) 不可变对象(只存状态,无业务逻辑)
负责人(Caretaker) 管理备忘录(保存 / 获取 / 删除),但不修改、不访问备忘录的内部状态 撤销栈(管理所有快照) 管理器类(如UndoManagerSaveManager
核心 UML 类图

二、文档编辑器撤销

以 "简单文档编辑器" 为例,实现备忘录模式的核心逻辑 ------ 支持保存文档快照、撤销到上一版本,覆盖所有核心角色。

1. 步骤 1:定义备忘录(文档快照)
复制代码
/**
 * 备忘录:文档快照(存储文档的状态,不可变)
 * 核心:只暴露给原发器获取状态的方法,不对外暴露修改方法
 */
public class DocumentMemento {
    // 文档状态:内容、字体、字号
    private final String content;
    private final String font;
    private final int fontSize;

    // 构造函数:仅原发器可调用,初始化所有状态
    public DocumentMemento(String content, String font, int fontSize) {
        this.content = content;
        this.font = font;
        this.fontSize = fontSize;
    }

    // 仅提供getter,不提供setter(保证状态不可变)
    public String getContent() {
        return content;
    }

    public String getFont() {
        return font;
    }

    public int getFontSize() {
        return fontSize;
    }
}
2. 步骤 2:定义原发器(文档对象)
复制代码
/**
 * 原发器:文档对象(生成快照、恢复快照)
 */
public class Document {
    // 文档内部状态(需要保存的属性)
    private String content;
    private String font;
    private int fontSize;

    /**
     * 生成备忘录(保存当前状态)
     */
    public DocumentMemento createMemento() {
        System.out.println("【原发器】生成文档快照:内容=" + content + ",字体=" + font + ",字号=" + fontSize);
        // 将当前所有状态封装到备忘录
        return new DocumentMemento(content, font, fontSize);
    }

    /**
     * 从备忘录恢复状态
     */
    public void restoreMemento(DocumentMemento memento) {
        this.content = memento.getContent();
        this.font = memento.getFont();
        this.fontSize = memento.getFontSize();
        System.out.println("【原发器】恢复文档快照:内容=" + content + ",字体=" + font + ",字号=" + fontSize);
    }

    // 业务方法:修改文档内容
    public void setContent(String content) {
        this.content = content;
    }

    // 业务方法:修改字体样式
    public void setFontStyle(String font, int fontSize) {
        this.font = font;
        this.fontSize = fontSize;
    }

    // 打印当前状态
    public void printState() {
        System.out.println("【当前文档状态】内容:" + content + ",字体:" + font + ",字号:" + fontSize);
    }

    // Getter(仅用于测试)
    public String getContent() {
        return content;
    }

    public String getFont() {
        return font;
    }

    public int getFontSize() {
        return fontSize;
    }
}
3. 步骤 3:定义负责人(撤销管理器)
复制代码
import java.util.Stack;

/**
 * 负责人:撤销管理器(管理所有备忘录,不修改/访问备忘录内部状态)
 */
public class UndoManager {
    // 用栈存储备忘录(先进后出,支持撤销)
    private final Stack<DocumentMemento> mementoStack = new Stack<>();

    /**
     * 保存备忘录(入栈)
     */
    public void saveMemento(DocumentMemento memento) {
        mementoStack.push(memento);
        System.out.println("【负责人】保存快照,当前快照数量:" + mementoStack.size());
    }

    /**
     * 获取最后一个备忘录(出栈,用于撤销)
     */
    public DocumentMemento getLastMemento() {
        if (mementoStack.isEmpty()) {
            System.out.println("【负责人】无快照可恢复");
            return null;
        }
        DocumentMemento memento = mementoStack.pop();
        System.out.println("【负责人】获取最后一个快照,剩余快照数量:" + mementoStack.size());
        return memento;
    }

    /**
     * 清空所有快照
     */
    public void clear() {
        mementoStack.clear();
        System.out.println("【负责人】清空所有快照");
    }
}
4. 客户端(测试编辑器撤销功能)
复制代码
/**
 * 客户端:测试文档编辑器的撤销功能
 */
public class MementoClient {
    public static void main(String[] args) {
        // 1. 创建原发器(文档)
        Document document = new Document();
        // 2. 创建负责人(撤销管理器)
        UndoManager undoManager = new UndoManager();

        // 3. 第一次编辑:初始化文档
        document.setContent("Hello World!");
        document.setFontStyle("宋体", 12);
        undoManager.saveMemento(document.createMemento()); // 保存快照1
        document.printState();

        // 4. 第二次编辑:修改内容
        document.setContent("Hello 备忘录模式!");
        undoManager.saveMemento(document.createMemento()); // 保存快照2
        document.printState();

        // 5. 第三次编辑:修改字体
        document.setFontStyle("微软雅黑", 14);
        undoManager.saveMemento(document.createMemento()); // 保存快照3
        document.printState();

        // 6. 第一次撤销:恢复到修改字体前
        System.out.println("\n======= 第一次撤销 =======");
        DocumentMemento m1 = undoManager.getLastMemento();
        if (m1 != null) {
            document.restoreMemento(m1);
            document.printState();
        }

        // 7. 第二次撤销:恢复到修改内容前
        System.out.println("\n======= 第二次撤销 =======");
        DocumentMemento m2 = undoManager.getLastMemento();
        if (m2 != null) {
            document.restoreMemento(m2);
            document.printState();
        }

        // 8. 第三次撤销:无快照
        System.out.println("\n======= 第三次撤销 =======");
        DocumentMemento m3 = undoManager.getLastMemento();
        if (m3 != null) {
            document.restoreMemento(m3);
        }
    }
}
输出结果
复制代码
【原发器】生成文档快照:内容=Hello World!,字体=宋体,字号=12
【负责人】保存快照,当前快照数量:1
【当前文档状态】内容:Hello World!,字体:宋体,字号:12
【原发器】生成文档快照:内容=Hello 备忘录模式!,字体=宋体,字号=12
【负责人】保存快照,当前快照数量:2
【当前文档状态】内容:Hello 备忘录模式!,字体:宋体,字号:12
【原发器】生成文档快照:内容=Hello 备忘录模式!,字体=微软雅黑,字号=14
【负责人】保存快照,当前快照数量:3
【当前文档状态】内容:Hello 备忘录模式!,字体:微软雅黑,字号:14

======= 第一次撤销 =======
【负责人】获取最后一个快照,剩余快照数量:2
【原发器】恢复文档快照:内容=Hello 备忘录模式!,字体=宋体,字号=12
【当前文档状态】内容:Hello 备忘录模式!,字体:宋体,字号:12

======= 第二次撤销 =======
【负责人】获取最后一个快照,剩余快照数量:1
【原发器】恢复文档快照:内容=Hello World!,字体=宋体,字号=12
【当前文档状态】内容:Hello World!,字体:宋体,字号:12

======= 第三次撤销 =======
【负责人】获取最后一个快照,剩余快照数量:0
【原发器】恢复文档快照:内容=null,字体=null,字号=0
【当前文档状态】内容:null,字体:null,字号:0

三、Spring 实战版(订单状态快照 + 回滚)

在业务开发中,备忘录模式最核心的实战场景是订单状态快照与回滚(如支付失败回滚订单状态、订单取消恢复库存)。以下实现一个 "订单状态快照管理器",支持保存订单不同阶段的状态,失败时恢复到指定状态。

1. 依赖准备(Spring Boot)
复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>3.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
2. 核心模型定义
复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 订单状态枚举
 */
public enum OrderStatus {
    CREATE("已创建"),
    PAY("已支付"),
    SHIP("已发货"),
    RECEIVE("已收货"),
    CANCEL("已取消");

    private final String desc;

    OrderStatus(String desc) {
        this.desc = desc;
    }

    public String getDesc() {
        return desc;
    }
}

/**
 * 备忘录:订单状态快照(不可变)
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderMemento {
    private String orderId; // 订单ID
    private OrderStatus status; // 订单状态
    private double amount; // 订单金额
    private String createTime; // 快照创建时间
    private String operator; // 操作人
}

/**
 * 原发器:订单对象
 */
@Data
public class Order {
    private String orderId;
    private OrderStatus status;
    private double amount;
    private String userId;
    private String createTime;

    /**
     * 生成订单状态快照
     * @param operator 操作人
     * @return 订单快照
     */
    public OrderMemento createMemento(String operator) {
        System.out.println("【原发器-订单】生成快照 | 订单ID:" + orderId + ",状态:" + status.getDesc());
        return new OrderMemento(
                orderId,
                status,
                amount,
                createTime,
                operator
        );
    }

    /**
     * 从快照恢复订单状态
     */
    public void restoreMemento(OrderMemento memento) {
        this.status = memento.getStatus();
        System.out.println("【原发器-订单】恢复快照 | 订单ID:" + orderId + ",恢复后状态:" + status.getDesc());
    }

    /**
     * 业务方法:更新订单状态
     */
    public void updateStatus(OrderStatus status) {
        this.status = status;
        System.out.println("【原发器-订单】更新状态 | 订单ID:" + orderId + ",新状态:" + status.getDesc());
    }
}
3. 负责人(订单快照管理器,Spring Bean)
复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

/**
 * 负责人:订单快照管理器(Spring Bean,管理所有订单的快照)
 * 核心:按订单ID分类存储快照,支持按状态恢复
 */
@Slf4j
@Component
public class OrderMementoManager {
    // 存储结构:订单ID → 快照队列(按时间顺序存储)
    private final Map<String, Queue<OrderMemento>> orderMementoMap = new HashMap<>();

    /**
     * 保存订单快照
     */
    public void saveMemento(String orderId, OrderMemento memento) {
        // 初始化订单的快照队列
        orderMementoMap.computeIfAbsent(orderId, k -> new LinkedList<>());
        // 添加快照到队列
        orderMementoMap.get(orderId).offer(memento);
        log.info("【负责人-快照管理器】保存订单快照 | 订单ID:{},当前快照数:{}",
                orderId, orderMementoMap.get(orderId).size());
    }

    /**
     * 根据状态恢复订单(找到指定状态的快照并恢复)
     */
    public OrderMemento restoreByStatus(String orderId, OrderStatus targetStatus) {
        Queue<OrderMemento> mementos = orderMementoMap.get(orderId);
        if (mementos == null || mementos.isEmpty()) {
            log.error("【负责人-快照管理器】订单无快照可恢复 | 订单ID:{}", orderId);
            return null;
        }

        // 遍历快照队列,找到指定状态的快照
        for (OrderMemento memento : mementos) {
            if (memento.getStatus() == targetStatus) {
                log.info("【负责人-快照管理器】找到指定状态快照 | 订单ID:{},状态:{}",
                        orderId, targetStatus.getDesc());
                return memento;
            }
        }

        log.error("【负责人-快照管理器】未找到指定状态快照 | 订单ID:{},目标状态:{}",
                orderId, targetStatus.getDesc());
        return null;
    }

    /**
     * 获取订单最后一次快照(用于撤销最后一步操作)
     */
    public OrderMemento getLastMemento(String orderId) {
        Queue<OrderMemento> mementos = orderMementoMap.get(orderId);
        if (mementos == null || mementos.isEmpty()) {
            log.error("【负责人-快照管理器】订单无快照可恢复 | 订单ID:{}", orderId);
            return null;
        }

        // 队列尾部是最后一次快照
        OrderMemento lastMemento = ((LinkedList<OrderMemento>) mementos).getLast();
        log.info("【负责人-快照管理器】获取最后一次快照 | 订单ID:{},状态:{}",
                orderId, lastMemento.getStatus().getDesc());
        return lastMemento;
    }

    /**
     * 清空订单快照
     */
    public void clearMementos(String orderId) {
        orderMementoMap.remove(orderId);
        log.info("【负责人-快照管理器】清空订单快照 | 订单ID:{}", orderId);
    }
}
4. 订单服务(业务逻辑)
复制代码
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 订单服务(业务入口,整合原发器和负责人)
 */
@Service
public class OrderService {
    @Resource
    private OrderMementoManager mementoManager;

    /**
     * 创建订单并保存初始快照
     */
    public Order createOrder(String orderId, String userId, double amount) {
        Order order = new Order();
        order.setOrderId(orderId);
        order.setUserId(userId);
        order.setAmount(amount);
        order.setStatus(OrderStatus.CREATE);
        order.setCreateTime(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

        // 保存初始快照
        OrderMemento memento = order.createMemento("system");
        mementoManager.saveMemento(orderId, memento);

        return order;
    }

    /**
     * 支付订单(失败则回滚到创建状态)
     */
    public boolean payOrder(Order order, String operator) {
        // 保存支付前的快照
        mementoManager.saveMemento(order.getOrderId(), order.createMemento(operator));

        try {
            // 模拟支付逻辑:金额>1000时支付失败
            if (order.getAmount() > 1000) {
                throw new RuntimeException("支付金额超限,单笔支付最大1000元");
            }

            // 支付成功,更新状态
            order.updateStatus(OrderStatus.PAY);
            // 保存支付后的快照
            mementoManager.saveMemento(order.getOrderId(), order.createMemento(operator));
            return true;
        } catch (Exception e) {
            log.error("【订单服务】支付失败 | 订单ID:{},原因:{}", order.getOrderId(), e.getMessage());
            // 恢复到创建状态
            OrderMemento createMemento = mementoManager.restoreByStatus(order.getOrderId(), OrderStatus.CREATE);
            if (createMemento != null) {
                order.restoreMemento(createMemento);
            }
            return false;
        }
    }

    /**
     * 取消订单(恢复到创建状态)
     */
    public void cancelOrder(Order order, String operator) {
        // 保存取消前的快照
        mementoManager.saveMemento(order.getOrderId(), order.createMemento(operator));
        // 恢复到创建状态
        OrderMemento createMemento = mementoManager.restoreByStatus(order.getOrderId(), OrderStatus.CREATE);
        if (createMemento != null) {
            order.restoreMemento(createMemento);
        }
        // 更新为取消状态
        order.updateStatus(OrderStatus.CANCEL);
    }
}
5. 客户端(Spring Boot 测试)
复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * 客户端:测试订单快照与回滚
 */
@SpringBootApplication
public class SpringMementoDemoApplication {
    public static void main(String[] args) {
        // 1. 启动Spring容器
        ConfigurableApplicationContext context = SpringApplication.run(SpringMementoDemoApplication.class, args);
        OrderService orderService = context.getBean(OrderService.class);

        // 2. 测试1:正常支付(金额<1000)
        System.out.println("======= 测试1:正常支付 =======");
        Order order1 = orderService.createOrder("O20240312001", "U1001", 800.0);
        boolean payResult1 = orderService.payOrder(order1, "U1001");
        System.out.println("支付结果:" + payResult1 + ",订单最终状态:" + order1.getStatus().getDesc());

        // 3. 测试2:支付失败(金额>1000),自动回滚
        System.out.println("\n======= 测试2:支付失败回滚 =======");
        Order order2 = orderService.createOrder("O20240312002", "U1002", 1200.0);
        boolean payResult2 = orderService.payOrder(order2, "U1002");
        System.out.println("支付结果:" + payResult2 + ",订单最终状态:" + order2.getStatus().getDesc());

        // 4. 测试3:取消订单(恢复到创建状态后标记为取消)
        System.out.println("\n======= 测试3:取消订单 =======");
        orderService.cancelOrder(order1, "U1001");
        System.out.println("订单最终状态:" + order1.getStatus().getDesc());

        context.close();
    }
}
输出结果
复制代码
======= 测试1:正常支付 =======
【原发器-订单】生成快照 | 订单ID:O20240312001,状态:已创建
【负责人-快照管理器】保存订单快照 | 订单ID:O20240312001,当前快照数:1
【原发器-订单】生成快照 | 订单ID:O20240312001,状态:已创建
【负责人-快照管理器】保存订单快照 | 订单ID:O20240312001,当前快照数:2
【原发器-订单】更新状态 | 订单ID:O20240312001,新状态:已支付
【原发器-订单】生成快照 | 订单ID:O20240312001,状态:已支付
【负责人-快照管理器】保存订单快照 | 订单ID:O20240312001,当前快照数:3
支付结果:true,订单最终状态:已支付

======= 测试2:支付失败回滚 =======
【原发器-订单】生成快照 | 订单ID:O20240312002,状态:已创建
【负责人-快照管理器】保存订单快照 | 订单ID:O20240312002,当前快照数:1
【原发器-订单】生成快照 | 订单ID:O20240312002,状态:已创建
【负责人-快照管理器】保存订单快照 | 订单ID:O20240312002,当前快照数:2
【订单服务】支付失败 | 订单ID:O20240312002,原因:支付金额超限,单笔支付最大1000元
【负责人-快照管理器】找到指定状态快照 | 订单ID:O20240312002,状态:已创建
【原发器-订单】恢复快照 | 订单ID:O20240312002,恢复后状态:已创建
支付结果:false,订单最终状态:已创建

======= 测试3:取消订单 =======
【原发器-订单】生成快照 | 订单ID:O20240312001,状态:已支付
【负责人-快照管理器】保存订单快照 | 订单ID:O20240312001,当前快照数:4
【负责人-快照管理器】找到指定状态快照 | 订单ID:O20240312001,状态:已创建
【原发器-订单】恢复快照 | 订单ID:O20240312001,恢复后状态:已创建
【原发器-订单】更新状态 | 订单ID:O20240312001,新状态:已取消
订单最终状态:已取消

四、备忘录模式的核心特点与适用场景

优点
  1. 封装性好:原发器的内部状态对外部隐藏,仅通过备忘录保存 / 恢复,符合迪米特法则;
  2. 状态可回溯:支持多步撤销、重做,可恢复到任意历史状态;
  3. 解耦状态管理:原发器只负责生成 / 恢复状态,状态的存储、管理由负责人完成;
  4. 支持事务回滚:操作失败时可快速恢复到操作前的状态,保证数据一致性;
  5. 快照不可变:备忘录通常设计为不可变对象,避免状态被意外修改。
缺点
  1. 内存开销大:如果原发器状态复杂、快照数量多,会占用大量内存(如编辑器的每一步操作都保存快照);
  2. 性能损耗:生成 / 恢复快照需要序列化 / 反序列化状态,频繁操作会影响性能;
  3. 状态同步问题:如果原发器的状态结构修改,历史快照可能无法兼容;
  4. 负责人无法优化快照:负责人只能管理快照,无法修改 / 压缩快照内容(如需优化需修改原发器)。
适用场景
  1. 撤销 / 重做功能:文档编辑器、代码编辑器、图形设计工具;
  2. 状态备份与恢复:游戏存档、系统配置备份、数据库备份;
  3. 事务回滚:数据库事务、支付流程、订单状态变更;
  4. 快照监控:系统运行状态快照、性能监控快照、日志快照;
  5. 原型模式结合:保存对象初始状态,快速复制多个相同状态的对象。

五、JDK/ Spring 中的原生应用(必须知道)

备忘录模式在框架中主要用于状态保存与恢复,以下是核心场景:

1. Java 序列化(Serializable)
  • 原发器:实现Serializable的对象;
  • 备忘录:序列化后的字节流 / 文件;
  • 负责人:序列化工具类(如ObjectOutputStream);
  • 核心逻辑:通过序列化保存对象状态,反序列化恢复状态(本质是备忘录模式的实现)。
2. Spring 事务管理(TransactionManager)
  • 原发器:数据库连接 / 数据记录;
  • 备忘录:事务日志(undo log/redo log);
  • 负责人:PlatformTransactionManager;
  • 核心逻辑:事务提交前保存数据快照,回滚时通过日志恢复数据。
3. Spring StateMachine(状态机)
  • 原发器:状态机上下文(StateContext);
  • 备忘录:状态快照(StateMachineAccess);
  • 负责人:状态机管理器(StateMachineManager);
  • 核心逻辑:保存状态机的历史状态,支持状态回滚、状态恢复。
4. Redis 持久化(RDB/AOF)
  • 原发器:Redis 内存数据;
  • 备忘录:RDB 快照文件 / AOF 日志;
  • 负责人:Redis 持久化管理器;
  • 核心逻辑:定期生成数据快照(RDB)或记录操作日志(AOF),重启时恢复数据。
5. 浏览器历史记录
  • 原发器:浏览器页面(URL、滚动位置、表单数据);
  • 备忘录:历史记录条目;
  • 负责人:浏览器历史记录管理器;
  • 核心逻辑:保存页面状态快照,支持前进 / 后退(撤销 / 重做)。

六、备忘录模式 vs 原型模式(易混淆点)

维度 备忘录模式 原型模式
核心目的 保存 / 恢复对象历史状态 复制对象当前状态,创建新对象
状态时效性 关注历史状态(过去的某个时刻) 关注当前状态(复制时的状态)
存储方式 外部存储(栈 / 队列 / 文件) 内存复制(新对象)
核心操作 保存(createMemento)、恢复(restore) 克隆(clone)
典型场景 撤销 / 重做、事务回滚、快照恢复 对象复制、原型实例、批量创建

总结

  1. 备忘录模式的核心是在不暴露对象内部结构的前提下,保存 / 恢复对象的历史状态,核心角色包括原发器(生成 / 恢复快照)、备忘录(存储状态)、负责人(管理快照);
  2. 备忘录需设计为不可变对象,避免状态被意外修改;负责人只管理快照,不访问 / 修改快照内部状态;
  3. 业务开发中,备忘录模式最实用的场景是订单状态回滚、支付事务回滚、编辑器撤销,需注意控制快照数量以减少内存开销;
  4. 备忘录模式常与序列化、事务管理结合使用,是实现数据一致性、状态可回溯的核心模式。
相关推荐
暴力求解2 小时前
Linux---ELF与库加载
linux·运维·服务器
顶点多余2 小时前
事务(数据库使用者角度的术语)
数据库·mysql
愚者游世2 小时前
alignof 和 alignas各版本异同
c++·学习·程序人生·职场和发展·visual studio
秃头摸鱼侠2 小时前
OpenClaw 团队级落地手册:规范、权限、安全、CI/CD 一体化实践
数据库·安全·ci/cd·ai
一行12 小时前
旧电脑重生:老i5装Ubuntu 保姆级步骤
服务器·windows·学习
卡梅德生物科技2 小时前
卡梅德生物科普:CD140a(PDGFRα)靶点深度解析:机制、药物研发与未来趋势
大数据·人工智能·面试·职场和发展·学习方法
yzx9910132 小时前
Ollama 完全使用指南:从零开始在本地部署大模型
数据库·mysql·github
pupudawang2 小时前
SQL-触发器(trigger)的详解以及代码演示
服务器·数据库·sql
IT研究所2 小时前
从工单到智能分析:AIGC运维助手应用价值
大数据·运维·数据库·人工智能·科技·低代码·自动化