最近看到一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到網站
一、基礎實現結構
- 角色定義與代碼骨架
備忘錄模式包含三個核心角色,其協作關系如下:
- Originator(發起人):需保存/恢復狀態的對象,負責創建和使用備忘錄。
- Memento(備忘錄):存儲發起人狀態的載體,通常設計為不可變類。
- Caretaker(管理者):管理備忘錄對象,不操作其內容。
代碼示例(文本編輯器場景):
// 發起人:文本編輯器
public class Editor {private String content;// 創建備忘錄public Memento save() {return new Memento(this.content);}// 從備忘錄恢復狀態public void restore(Memento memento) {this.content = memento.getState();}
}
// 備忘錄:存儲狀態
public class Memento {private final String state; // 不可變狀態public Memento(String state) { this.state = state; }public String getState() { return state; }
}
// 管理者:保存備忘錄列表
public class History {private List mementos = new ArrayList<>();public void add(Memento memento) { mementos.add(memento); }public Memento get(int index) { return mementos.get(index); }
}
- 狀態保存與恢復流程
// 客戶端調用示例
Editor editor = new Editor();
History history = new History();
editor.setContent("Initial text");
history.add(editor.save()); // 保存狀態1
editor.setContent("Modified text");
history.add(editor.save()); // 保存狀態2
// 恢復到狀態1
editor.restore(history.get(0));
System.out.println(editor.getContent()); // 輸出:Initial text
二、高級實現技巧
- 封裝性優化
- 黑箱實現:將
Memento
作為Originator
的內部類,限制外部訪問。public class Editor {private String content;// 內部類備忘錄public class EditorMemento {private final String state;private EditorMemento() { this.state = content; } // 僅內部可訪問}public EditorMemento save() { return new EditorMemento(); } }
- 窄接口與寬接口:通過包級私有訪問控制,僅允許
Originator
讀取Memento
內部狀態。
- 多狀態管理
使用棧結構實現撤銷/重做功能:
public class History {private Stack undoStack = new Stack<>();private Stack redoStack = new Stack<>();public void push(Memento memento) { undoStack.push(memento); }public Memento undo() { return undoStack.pop(); }public void redo(Memento memento) { redoStack.push(memento); }
}
- 資源優化
- 增量存儲:僅保存狀態差異而非全量數據。
- 序列化:通過
Serializable
接口持久化備忘錄至文件或數據庫。// 發起人支持序列化 public class SerializableOriginator extends Originator implements Serializable {private String state;// 實現序列化邏輯 }
三、實際應用示例
- 游戲存檔系統
// 發起人:游戲角色
public class Gamer {private int level;private Inventory items;public GamerMemento save() {return new GamerMemento(level, items.clone());}public void restore(GamerMemento memento) {this.level = memento.getLevel();this.items = memento.getItems();}// 備忘錄類(內部類)public static class GamerMemento {private final int level;private final Inventory items;// 構造函數與Getter}
}
- 數據庫事務回滾
public class DatabaseTransaction {private TableData originalData;public TransactionMemento snapshot() {return new TransactionMemento(originalData.clone());}public void rollback(TransactionMemento memento) {this.originalData = memento.getData();}
}
四、測試與調試
- 單元測試要點
- 狀態一致性驗證:使用斷言檢查恢復后的狀態是否與預期一致。
assertEquals("Initial text", editor.getContent());
- 異常場景測試:模擬空備忘錄或非法恢復操作。
assertThrows(IllegalStateException.class, () -> editor.restore(null));
- 性能監控
- 內存占用分析:通過Profiler工具監控備忘錄對象的數量與大小。
- 優化策略:限制備忘錄歷史數量(如僅保存最近10個狀態)。
五、常見問題與解決方案
問題 | 解決方案 |
---|---|
內存溢出 | 使用LRU算法淘汰舊備忘錄,或采用增量存儲。 |
狀態不一致 | 確保Memento 不可變,避免外部修改。 |
跨會話恢復失敗 | 將備忘錄序列化至持久化存儲(如文件或數據庫)。 |
六、總結
備忘錄模式通過狀態快照與封裝隔離機制,為撤銷、回滾等場景提供了靈活的解決方案。實現時需注重:
- 職責分離:明確
Originator
、Memento
、Caretaker
的邊界。 - 資源管理:根據場景選擇內存存儲或持久化方案。
- 封裝性保護:通過內部類或訪問控制限制備忘錄訪問。
該模式廣泛應用于編輯器、游戲、數據庫等領域,是實現無副作用狀態管理的核心工具。