设计模式-备忘录模式详解

备忘录模式详解

目录


模式简介

定义

备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后可以将该对象恢复到原先保存的状态。

核心思想

  • 状态保存:在不破坏对象封装性的前提下,保存对象的内部状态
  • 状态恢复:在需要时能够将对象恢复到之前保存的状态
  • 历史管理:提供管理多个历史状态的能力

模式结构

  • Originator(发起人):需要保存状态的对象
  • Memento(备忘录):存储发起人内部状态的对象
  • Caretaker(管理者):负责保存和恢复备忘录的对象

核心流程

基本流程图

客户端请求 Originator创建状态 创建Memento保存状态 Caretaker管理Memento Originator状态改变 需要恢复时 Caretaker获取Memento Originator从Memento恢复 状态恢复完成

详细流程步骤

1. 状态保存流程

Originator当前状态 调用createMemento() 创建Memento对象 保存内部状态到Memento 返回Memento给Caretaker Caretaker存储Memento

2. 状态恢复流程

需要恢复状态 Caretaker获取Memento 调用Originator.restoreMemento() Originator从Memento读取状态 恢复内部状态 状态恢复完成

代码示例流程

java 复制代码
// 1. 创建原始对象
Originator originator = new Originator();
originator.setState("State1");

// 2. 保存状态
Memento memento = originator.createMemento();
Caretaker caretaker = new Caretaker();
caretaker.setMemento(memento);

// 3. 改变状态
originator.setState("State2");

// 4. 恢复状态
originator.restoreMemento(caretaker.getMemento());

重难点分析

重点分析

1. 封装性保护
java 复制代码
public class Originator {
    private String state;
  
    // 只有Originator能创建和访问Memento的内部状态
    public Memento createMemento() {
        return new Memento(this.state);
    }
  
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }
}

// Memento类设计为包私有或内部类
class Memento {
    private final String state;
  
    Memento(String state) {
        this.state = state;
    }
  
    String getState() {
        return state;
    }
}
2. 状态管理策略
java 复制代码
public class Caretaker {
    private List<Memento> mementos = new ArrayList<>();
    private int currentIndex = -1;
  
    // 保存状态
    public void saveMemento(Memento memento) {
        // 删除当前位置之后的所有状态
        mementos = mementos.subList(0, currentIndex + 1);
        mementos.add(memento);
        currentIndex++;
    }
  
    // 撤销
    public Memento undo() {
        if (currentIndex > 0) {
            currentIndex--;
            return mementos.get(currentIndex);
        }
        return null;
    }
  
    // 重做
    public Memento redo() {
        if (currentIndex < mementos.size() - 1) {
            currentIndex++;
            return mementos.get(currentIndex);
        }
        return null;
    }
}

难点分析

1. 内存管理
java 复制代码
public class Caretaker {
    private static final int MAX_MEMENTOS = 50;
    private List<Memento> mementos = new ArrayList<>();
  
    public void saveMemento(Memento memento) {
        mementos.add(memento);
      
        // 限制历史记录数量,防止内存溢出
        if (mementos.size() > MAX_MEMENTOS) {
            mementos.remove(0);
        }
    }
}
2. 深拷贝与浅拷贝
java 复制代码
public class Originator {
    private List<String> stateList;
  
    public Memento createMemento() {
        // 深拷贝,避免状态被意外修改
        List<String> copyList = new ArrayList<>(stateList);
        return new Memento(copyList);
    }
  
    public void restoreMemento(Memento memento) {
        // 深拷贝恢复
        this.stateList = new ArrayList<>(memento.getStateList());
    }
}
3. 并发安全
java 复制代码
public class Caretaker {
    private final List<Memento> mementos = Collections.synchronizedList(new ArrayList<>());
    private volatile int currentIndex = -1;
  
    public synchronized void saveMemento(Memento memento) {
        mementos.add(memento);
        currentIndex++;
    }
  
    public synchronized Memento undo() {
        if (currentIndex > 0) {
            currentIndex--;
            return mementos.get(currentIndex);
        }
        return null;
    }
}

Spring中的源码分析

1. 事务回滚机制

核心类:TransactionSynchronizationManager
java 复制代码
public abstract class TransactionSynchronizationManager {
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = 
        new NamedThreadLocal<>("Transaction synchronizations");
  
    // 注册同步回调,类似备忘录模式的状态保存
    public static void registerSynchronization(TransactionSynchronization synchronization) {
        Set<TransactionSynchronization> synchs = synchronizations.get();
        if (synchs == null) {
            synchs = new LinkedHashSet<>();
            synchronizations.set(synchs);
        }
        synchs.add(synchronization);
    }
}
事务状态保存与恢复
java 复制代码
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager {
  
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;
      
        try {
            // 保存当前状态(类似Memento)
            if (txObject.getConnectionHolder() == null) {
                Connection newCon = obtainDataSource().getConnection();
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }
          
            // 设置事务隔离级别
            prepareTransactionalConnection(con, definition);
          
        } catch (Throwable ex) {
            // 状态恢复(类似restoreMemento)
            if (txObject.isNewConnectionHolder()) {
                DataSourceUtils.releaseConnection(con, getDataSource());
            }
            throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
        }
    }
}

2. 配置属性管理

核心类:ConfigurationPropertiesBindingPostProcessor
java 复制代码
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor {
  
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 保存原始配置状态
        ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
            bean.getClass(), ConfigurationProperties.class);
      
        if (annotation != null) {
            // 创建配置备忘录
            ConfigurationPropertiesState state = createConfigurationState(bean);
            // 绑定新配置
            bindConfigurationProperties(bean, state);
        }
      
        return bean;
    }
  
    // 配置状态保存(备忘录模式)
    private ConfigurationPropertiesState createConfigurationState(Object bean) {
        return new ConfigurationPropertiesState(bean);
    }
}

3. 会话管理

核心类:HttpSession
java 复制代码
public class StandardSession implements HttpSession, HttpSessionActivationListener {
  
    // 会话状态保存
    public void setAttribute(String name, Object value) {
        // 保存属性到会话中
        if (value == null) {
            removeAttribute(name);
        } else {
            attributes.put(name, value);
            // 标记会话为已修改(状态变更)
            if (isValid) {
                manager.touch(this);
            }
        }
    }
  
    // 会话状态恢复
    public Object getAttribute(String name) {
        if (!isValid) {
            throw new IllegalStateException("Session has been invalidated");
        }
        return attributes.get(name);
    }
}

4. 缓存状态管理

核心类:ConcurrentMapCache
java 复制代码
public class ConcurrentMapCache extends AbstractValueAdaptingCache {
    private final ConcurrentMap<Object, Object> store;
  
    @Override
    protected Object lookup(Object key) {
        // 从缓存中恢复状态
        return this.store.get(key);
    }
  
    @Override
    public void put(Object key, Object value) {
        // 保存状态到缓存
        this.store.put(key, toStoreValue(value));
    }
  
    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        // 条件保存,类似备忘录的条件创建
        Object existing = this.store.putIfAbsent(key, toStoreValue(value));
        return toValueWrapper(existing);
    }
}

具体使用场景

1. 文本编辑器撤销/重做功能

java 复制代码
// 文本编辑器备忘录模式实现
public class TextEditor {
    private String content;
    private Caretaker caretaker = new Caretaker();
  
    public void setContent(String content) {
        // 保存当前状态
        caretaker.saveMemento(createMemento());
        this.content = content;
    }
  
    public void undo() {
        Memento memento = caretaker.undo();
        if (memento != null) {
            restoreMemento(memento);
        }
    }
  
    public void redo() {
        Memento memento = caretaker.redo();
        if (memento != null) {
            restoreMemento(memento);
        }
    }
  
    private Memento createMemento() {
        return new Memento(content);
    }
  
    private void restoreMemento(Memento memento) {
        this.content = memento.getContent();
    }
}

2. 游戏存档系统

java 复制代码
// 游戏状态备忘录
public class GameState {
    private int level;
    private int score;
    private String playerName;
    private List<String> inventory;
  
    public GameMemento saveGame() {
        return new GameMemento(level, score, playerName, new ArrayList<>(inventory));
    }
  
    public void loadGame(GameMemento memento) {
        this.level = memento.getLevel();
        this.score = memento.getScore();
        this.playerName = memento.getPlayerName();
        this.inventory = new ArrayList<>(memento.getInventory());
    }
}

// 游戏管理器
public class GameManager {
    private GameState currentState;
    private List<GameMemento> saveSlots = new ArrayList<>();
  
    public void saveGame(int slot) {
        if (slot >= 0 && slot < saveSlots.size()) {
            saveSlots.set(slot, currentState.saveGame());
        } else {
            saveSlots.add(currentState.saveGame());
        }
    }
  
    public void loadGame(int slot) {
        if (slot >= 0 && slot < saveSlots.size()) {
            currentState.loadGame(saveSlots.get(slot));
        }
    }
}

3. 数据库事务回滚

java 复制代码
// 数据库操作备忘录
public class DatabaseTransaction {
    private List<DatabaseOperation> operations = new ArrayList<>();
    private Map<String, Object> originalValues = new HashMap<>();
  
    public void executeUpdate(String sql, Object... params) {
        // 保存原始值
        saveOriginalValues(sql, params);
        // 执行更新
        operations.add(new UpdateOperation(sql, params));
    }
  
    public void rollback() {
        // 恢复原始状态
        for (int i = operations.size() - 1; i >= 0; i--) {
            operations.get(i).rollback(originalValues);
        }
        operations.clear();
    }
  
    private void saveOriginalValues(String sql, Object... params) {
        // 查询并保存原始值
        String key = generateKey(sql, params);
        Object originalValue = queryOriginalValue(sql, params);
        originalValues.put(key, originalValue);
    }
}

4. 配置管理系统

java 复制代码
// 配置备忘录模式
public class ConfigurationManager {
    private Map<String, Object> currentConfig;
    private Stack<ConfigurationMemento> history = new Stack<>();
  
    public void updateConfiguration(String key, Object value) {
        // 保存当前配置状态
        history.push(createMemento());
      
        // 更新配置
        currentConfig.put(key, value);
    }
  
    public void rollbackConfiguration() {
        if (!history.isEmpty()) {
            ConfigurationMemento memento = history.pop();
            restoreMemento(memento);
        }
    }
  
    private ConfigurationMemento createMemento() {
        return new ConfigurationMemento(new HashMap<>(currentConfig));
    }
  
    private void restoreMemento(ConfigurationMemento memento) {
        this.currentConfig = new HashMap<>(memento.getConfig());
    }
}

5. 表单数据恢复

java 复制代码
// 表单备忘录模式
public class FormManager {
    private Map<String, Object> formData;
    private List<FormMemento> history = new ArrayList<>();
    private int currentIndex = -1;
  
    public void saveFormState() {
        // 删除当前位置之后的历史
        if (currentIndex < history.size() - 1) {
            history = history.subList(0, currentIndex + 1);
        }
      
        // 保存当前状态
        FormMemento memento = new FormMemento(new HashMap<>(formData));
        history.add(memento);
        currentIndex++;
    }
  
    public void undo() {
        if (currentIndex > 0) {
            currentIndex--;
            restoreFormState(history.get(currentIndex));
        }
    }
  
    public void redo() {
        if (currentIndex < history.size() - 1) {
            currentIndex++;
            restoreFormState(history.get(currentIndex));
        }
    }
  
    private void restoreFormState(FormMemento memento) {
        this.formData = new HashMap<>(memento.getFormData());
        // 更新UI显示
        updateFormUI();
    }
}

面试高频点

1. 基本概念类

Q: 什么是备忘录模式?

A: 备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后可以将该对象恢复到原先保存的状态。

Q: 备忘录模式的核心组件有哪些?

A:

  • Originator(发起人):需要保存状态的对象
  • Memento(备忘录):存储发起人内部状态的对象
  • Caretaker(管理者):负责保存和恢复备忘录的对象

2. 设计原理类

Q: 备忘录模式如何保证封装性?

A:

java 复制代码
// 通过包私有或内部类限制访问
class Memento {
    private final String state;
  
    // 只有同一个包或内部类能访问
    Memento(String state) {
        this.state = state;
    }
  
    String getState() {
        return state;
    }
}

// Originator控制Memento的创建和访问
public class Originator {
    public Memento createMemento() {
        return new Memento(this.state);
    }
  
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }
}
Q: 备忘录模式与命令模式的区别?

A:

  • 备忘录模式:关注对象状态的保存和恢复
  • 命令模式:关注请求的封装和调用
  • 备忘录模式:状态是数据,Memento存储数据
  • 命令模式:状态是操作,Command封装操作

3. 实现细节类

Q: 如何实现深拷贝的备忘录?

A:

java 复制代码
public class Originator {
    private List<String> stateList;
  
    public Memento createMemento() {
        // 深拷贝列表
        List<String> copyList = new ArrayList<>();
        for (String item : stateList) {
            copyList.add(new String(item));
        }
        return new Memento(copyList);
    }
  
    public void restoreMemento(Memento memento) {
        // 深拷贝恢复
        this.stateList = new ArrayList<>();
        for (String item : memento.getStateList()) {
            this.stateList.add(new String(item));
        }
    }
}
Q: 如何管理多个历史状态?

A:

java 复制代码
public class Caretaker {
    private List<Memento> mementos = new ArrayList<>();
    private int currentIndex = -1;
    private static final int MAX_HISTORY = 50;
  
    public void saveMemento(Memento memento) {
        // 删除当前位置之后的历史
        if (currentIndex < mementos.size() - 1) {
            mementos = mementos.subList(0, currentIndex + 1);
        }
      
        mementos.add(memento);
        currentIndex++;
      
        // 限制历史数量
        if (mementos.size() > MAX_HISTORY) {
            mementos.remove(0);
            currentIndex--;
        }
    }
  
    public Memento undo() {
        if (currentIndex > 0) {
            currentIndex--;
            return mementos.get(currentIndex);
        }
        return null;
    }
  
    public Memento redo() {
        if (currentIndex < mementos.size() - 1) {
            currentIndex++;
            return mementos.get(currentIndex);
        }
        return null;
    }
}

4. 性能优化类

Q: 备忘录模式可能遇到哪些性能问题?

A:

  1. 内存占用:大量历史状态占用内存
  2. 深拷贝开销:复杂对象的深拷贝成本高
  3. 序列化开销:网络传输或持久化时的序列化成本

解决方案:

java 复制代码
public class OptimizedCaretaker {
    private static final int MAX_MEMENTOS = 20;
    private List<Memento> mementos = new ArrayList<>();
  
    public void saveMemento(Memento memento) {
        // 限制历史数量
        if (mementos.size() >= MAX_MEMENTOS) {
            mementos.remove(0);
        }
        mementos.add(memento);
    }
  
    // 使用序列化进行持久化
    public void saveToFile(String filename) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename))) {
            out.writeObject(mementos);
        }
    }
  
    public void loadFromFile(String filename) throws IOException, ClassNotFoundException {
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {
            mementos = (List<Memento>) in.readObject();
        }
    }
}

5. 实际应用类

Q: 在Spring框架中,哪些地方使用了备忘录模式的思想?

A:

  1. 事务管理:保存事务开始前的状态,支持回滚
  2. 会话管理:HttpSession保存用户状态
  3. 配置管理:ConfigurationProperties的状态保存
  4. 缓存管理:缓存状态的保存和恢复
Q: 备忘录模式在微服务架构中的应用?

A:

java 复制代码
// 分布式状态管理
public class DistributedStateManager {
    private RedisTemplate<String, Object> redisTemplate;
  
    public void saveState(String serviceId, String stateId, Object state) {
        // 保存到Redis
        String key = "state:" + serviceId + ":" + stateId;
        redisTemplate.opsForValue().set(key, state, Duration.ofHours(24));
    }
  
    public Object restoreState(String serviceId, String stateId) {
        String key = "state:" + serviceId + ":" + stateId;
        return redisTemplate.opsForValue().get(key);
    }
  
    // 支持状态版本管理
    public void saveStateWithVersion(String serviceId, String stateId, Object state, int version) {
        String key = "state:" + serviceId + ":" + stateId + ":" + version;
        redisTemplate.opsForValue().set(key, state, Duration.ofDays(7));
    }
}

6. 设计权衡类

Q: 备忘录模式的优缺点?

A:
优点:

  • 保护封装性,不暴露对象内部实现
  • 提供状态恢复机制
  • 支持撤销/重做功能
  • 状态管理集中化

缺点:

  • 内存占用较大
  • 复杂对象的深拷贝成本高
  • 状态管理复杂度增加
  • 可能影响性能
Q: 什么时候不应该使用备忘录模式?

A:

  1. 状态变化频繁:每次变化都保存状态会消耗大量内存
  2. 对象状态简单:简单状态不需要复杂的状态管理
  3. 性能要求极高:状态保存和恢复可能影响性能
  4. 状态不可逆:某些状态一旦改变就不应该恢复

使用总结

适用场景总结

1. 必须使用备忘录模式的场景
  • 撤销/重做功能:文本编辑器、图形编辑器
  • 游戏存档系统:保存和加载游戏状态
  • 事务回滚:数据库事务、业务操作回滚
  • 配置管理:系统配置的保存和恢复
  • 表单数据恢复:用户输入数据的恢复
2. 可以考虑使用的场景
  • 状态快照:定期保存系统状态
  • 调试支持:保存调试时的状态信息
  • 版本控制:对象状态的版本管理
  • 备份恢复:重要数据的备份和恢复

最佳实践建议

1. 状态管理策略
java 复制代码
// 建议:实现状态管理策略
public interface StateManagementStrategy {
    void saveState(Object state);
    Object restoreState();
    boolean canUndo();
    boolean canRedo();
}

// 内存策略
public class MemoryStateStrategy implements StateManagementStrategy {
    private Stack<Object> stateStack = new Stack<>();
  
    @Override
    public void saveState(Object state) {
        stateStack.push(state);
    }
  
    @Override
    public Object restoreState() {
        return stateStack.pop();
    }
}

// 持久化策略
public class PersistentStateStrategy implements StateManagementStrategy {
    private String filePath;
  
    @Override
    public void saveState(Object state) {
        // 保存到文件
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath))) {
            out.writeObject(state);
        } catch (IOException e) {
            throw new RuntimeException("Failed to save state", e);
        }
    }
}
2. 性能优化建议
java 复制代码
// 建议:实现状态压缩
public class CompressedMemento implements Serializable {
    private byte[] compressedState;
  
    public CompressedMemento(Object state) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(state);
            oos.close();
          
            // 压缩数据
            this.compressedState = compress(baos.toByteArray());
        } catch (IOException e) {
            throw new RuntimeException("Failed to compress state", e);
        }
    }
  
    public Object getState() {
        try {
            // 解压缩数据
            byte[] decompressed = decompress(compressedState);
            ByteArrayInputStream bais = new ByteArrayInputStream(decompressed);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("Failed to decompress state", e);
        }
    }
}
3. 错误处理建议
java 复制代码
// 建议:实现错误处理机制
public class RobustCaretaker {
    private List<Memento> mementos = new ArrayList<>();
    private int currentIndex = -1;
  
    public void saveMemento(Memento memento) {
        try {
            // 验证备忘录有效性
            if (memento == null) {
                throw new IllegalArgumentException("Memento cannot be null");
            }
          
            // 保存备忘录
            if (currentIndex < mementos.size() - 1) {
                mementos = mementos.subList(0, currentIndex + 1);
            }
            mementos.add(memento);
            currentIndex++;
          
        } catch (Exception e) {
            // 记录错误但不影响程序运行
            System.err.println("Failed to save memento: " + e.getMessage());
        }
    }
  
    public Memento undo() {
        try {
            if (currentIndex > 0) {
                currentIndex--;
                return mementos.get(currentIndex);
            }
        } catch (Exception e) {
            System.err.println("Failed to undo: " + e.getMessage());
        }
        return null;
    }
}

与其他模式的结合

1. 与命令模式结合
java 复制代码
// 可撤销的命令
public abstract class UndoableCommand implements Command {
    protected Memento memento;
  
    @Override
    public void execute() {
        // 保存执行前状态
        memento = createMemento();
        doExecute();
    }
  
    public void undo() {
        if (memento != null) {
            restoreMemento(memento);
        }
    }
  
    protected abstract void doExecute();
    protected abstract Memento createMemento();
    protected abstract void restoreMemento(Memento memento);
}
2. 与状态模式结合
java 复制代码
// 状态历史管理
public class StateHistoryManager {
    private List<StateMemento> stateHistory = new ArrayList<>();
    private int currentStateIndex = -1;
  
    public void saveState(State currentState) {
        StateMemento memento = new StateMemento(currentState);
        stateHistory.add(memento);
        currentStateIndex++;
    }
  
    public State restorePreviousState() {
        if (currentStateIndex > 0) {
            currentStateIndex--;
            return stateHistory.get(currentStateIndex).getState();
        }
        return null;
    }
}

总结

备忘录模式是一个强大的设计模式,特别适用于需要状态保存和恢复的场景。通过合理的设计和优化,可以有效地管理对象状态,提供良好的用户体验。在实际应用中,需要根据具体需求选择合适的实现策略,并注意性能优化和错误处理。

相关推荐
yujkss6 小时前
23种设计模式之【原型模式】-核心原理与 Java实践
java·设计模式·原型模式
phdsky6 小时前
【设计模式】命令模式
设计模式·命令模式
青草地溪水旁6 小时前
设计模式(C++)详解——命令模式(1)
c++·设计模式·命令模式
青草地溪水旁7 小时前
设计模式(C++)详解——命令模式(2)
c++·设计模式·命令模式
new_daimond7 小时前
设计模式-原型模式详解
设计模式·原型模式
星星点点洲7 小时前
【Golang】数据设计模式
开发语言·设计模式·golang
步行cgn8 小时前
责任链设计模式详解
设计模式
青草地溪水旁9 小时前
设计模式(C++)详解——职责链模式 (Chain of Responsibility)(2)
c++·设计模式·责任链模式
青草地溪水旁9 小时前
设计模式(C++)详解——职责链模式 (Chain of Responsibility)(1)
c++·设计模式·责任链模式