1. 程式人生 > 其它 >備忘錄模式(Memento Pattern) – 設計模式之行為模式

備忘錄模式(Memento Pattern) – 設計模式之行為模式

技術標籤:設計模式設計模式

備忘錄模式(Memento Pattern) – 設計模式之行為模式:

目錄

備忘錄模式(Memento Pattern)

類圖

例子:

過程:

類圖:

程式碼:

遊戲:Game

遊戲備份:GameHistory

玩家:Player

測試:

結果:

總結:

使用場景


備忘錄模式(Memento Pattern)

定義: Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.

在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可以將該物件恢復到原先儲存的狀態。

類圖

備忘錄模式通用類圖:

例子:

過程:

在玩遊戲的時候,經常會進行存檔,尤其難度高的,幾乎是過一關存檔一次。存檔,需要建立yige-備份的物件,跟遊戲物件相類似。有存檔和恢復的操作

類圖:

程式碼:

遊戲:Game

public class Game {
 private int nums;// 關卡
 private String content;// 遊戲的內容
 public Game(int nums, String content) {
 this.nums = nums;
 this.content = content;
 }
 public int getNums() {
 return nums;
 }
 public void setNums(int nums) {
 this.nums = nums;
 }
 public String getContent() {
 return content;
 }
 public void setContent(String content) {
 this.content = content;
 }
 public GameHistory createHistory() {
 return new GameHistory(nums, content);//建立歷史記錄
 }
 public void restoreHistory(GameHistory gameHistory){
 this.nums = gameHistory.getNums();
 this.content = gameHistory.getContent();
 }
}

遊戲備份:GameHistory

public class GameHistory {
 private int nums; // 用於備用關卡
 private String content;//用於備忘遊戲內容
 public GameHistory(int nums, String content) {
 this.nums = nums;
 this.content = content;
 }
 public int getNums() {
 return nums;
 }
 public String getContent() {
 return content;
 }
 public void setContent(String content) {
 this.content = content;
 }
}

玩家:Player

public class Player {
 private Game game;
 private List<GameHistory> gameHistoryRecords;// 歷史記錄列表
 public Player(Game game) {
 System.out.println("<<<開啟遊戲" + game.getNums());
 this.game = game;
 gameHistoryRecords = new ArrayList<>();// 初始化歷史記錄
 }
 public void savePoint(int num, String msg) {
 System.out.println("打到第"+num+"關了,及時儲存下");
 game.setNums(num);
 game.setContent(game.getContent() + msg);
 backup();//操作完成後儲存歷史記錄
 show();
 }
 private void backup() {
 gameHistoryRecords.add(game.createHistory());
 }
 private void show() {
 System.out.println(game.getNums() + " 內容 "+ game.getContent());
 }
 public void undo(int num) {// 跳到第x關,重新開始開
 System.out.println(">>>恢復操作");
 if (num < 0) {
 return;
 }
 Map<Integer, GameHistory> historyMap = toMapInfo();
 GameHistory gameHistory = historyMap.get(num);
 game.restoreHistory(gameHistory);//取出歷史記錄並恢復至文件
 show();
 }
 // (x, y) -> y 取最近儲存的,
 private Map<Integer, GameHistory> toMapInfo(){
 return ListUtils.emptyIfNull(gameHistoryRecords).stream()
 .collect(Collectors.toMap(GameHistory::getNums, f -> f, (x, y) -> y));
 }
}

測試:

public class PlayerTest {
 public static void main(String[] args) {
 Player player = new Player(new Game(3,"打到第三關,武力值為500"));
 player.savePoint(5, "打到第五關了,武力值五600");
 player.savePoint(9, "打到第九關了,武力值五1100");
 player.savePoint(10, "打到第十關了,武力值五1155");
 player.savePoint(17, "打到第十七關了,武力值五1500");
 System.out.println("=========");
 player.undo(9);
 player.savePoint(10, "打到第十關了,武力值五1300");
 player.savePoint(11, "打到第十一關了,武力值五1310");
 System.out.println("=========");
 player.undo(10);
 player.savePoint(12, "打到第十二關了,武力值五1400");
 }
}

結果:

<<<開啟遊戲3

打到第5關了,及時儲存下

5 內容 打到第三關,武力值為500打到第五關了,武力值五600

打到第9關了,及時儲存下

9 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100

打到第10關了,及時儲存下

10 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100打到第十關了,武力值五1155

打到第17關了,及時儲存下

17 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100打到第十關了,武力值五1155打到第十七關了,武力值五1500

=========

>>>恢復操作

9 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100

打到第10關了,及時儲存下

10 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100打到第十關了,武力值五1300

打到第11關了,及時儲存下

11 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100打到第十關了,武力值五1300打到第十一關了,武力值五1310

=========

>>>恢復操作

10 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100打到第十關了,武力值五1300

打到第12關了,及時儲存下

12 內容 打到第三關,武力值為500打到第五關了,武力值五600打到第九關了,武力值五1100打到第十關了,武力值五1300打到第十二關了,武力值五1400

總結:

優點:

1、給使用者提供了一種可以恢復狀態的機制,可以使使用者能夠比較方便地回到某個歷史的狀態。

2、實現了資訊的封裝,使得使用者不需要關心狀態的儲存細節。

缺點:消耗資源。如果類的成員變數過多,勢必會佔用比較大的資源,而且每一次儲存都會消耗一定的記憶體。為了節約記憶體,可使用原型模式+備忘錄模式

使用場景

1, 需要儲存和恢復資料的相關狀態場景

2, 提供一個可回滾(rollback)的操作;比如Word中的CTRL+Z組合鍵,IE瀏覽器中的後退按鈕,檔案管理器上的backspace鍵等。

3, 需要監控的副本場景中。例如要監控一個物件的屬性,但是監控又不應該作為系統的主業務來呼叫,它只是邊緣應用,即使出現監控不準、錯誤報警也影響不大,因此一般的做法是備份一個主執行緒中的物件,然後由分析程式來分析。

4,資料庫連線的事務管理就是用的備忘錄模式,想想看,如果你要實現一個JDBC驅動,你怎麼來實現事務?還不是用備忘錄模式嘛!

注意事項

1, 備忘錄的生命期

備忘錄創建出來就要在“最近”的程式碼中使用,要主動管理它的生命週期,建立就要使用,不使用就要立刻刪除其引用,等待垃圾回收器對它的回收處理。

2, 備忘錄的效能

不要在頻繁建立備份的場景中使用備忘錄模式(比如一個for迴圈中),原因有二:一是控制不了備忘錄建立的物件數量;二是大物件的建立是要消耗資源的,系統的效能需要考慮。因此,如果出現這樣的程式碼,設計者就應該好好想想怎麼修改架構了。