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

备忘录模式详解

目录


模式简介

定义

备忘录模式(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读取状态
恢复内部状态
状态恢复完成

代码示例流程

// 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. 封装性保护
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. 状态管理策略
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. 内存管理
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. 深拷贝与浅拷贝
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. 并发安全
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
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);
    }
}
事务状态保存与恢复
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
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
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
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. 文本编辑器撤销/重做功能

// 文本编辑器备忘录模式实现
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. 游戏存档系统

// 游戏状态备忘录
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. 数据库事务回滚

// 数据库操作备忘录
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. 配置管理系统

// 配置备忘录模式
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. 表单数据恢复

// 表单备忘录模式
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:

// 通过包私有或内部类限制访问
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:

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:

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. 序列化开销:网络传输或持久化时的序列化成本

解决方案:

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:

// 分布式状态管理
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. 状态管理策略
// 建议:实现状态管理策略
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. 性能优化建议
// 建议:实现状态压缩
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. 错误处理建议
// 建议:实现错误处理机制
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. 与命令模式结合
// 可撤销的命令
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. 与状态模式结合
// 状态历史管理
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;
    }
}

总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值