備忘錄這個(gè)詞匯大家應(yīng)該都不陌生,我就經(jīng)常使用備忘錄來(lái)記錄一些比較重要的或者容易遺忘的信息,與之相關(guān)的最常見(jiàn)的應(yīng)用有許多,比如游戲存檔,我們玩游戲的時(shí)候肯定有存檔功能,旨在下一次登錄游戲時(shí)可以從上次退出的地方繼續(xù)游戲,或者對(duì)復(fù)活點(diǎn)進(jìn)行存檔,如果掛掉了則可以讀取復(fù)活點(diǎn)的存檔信息重新開(kāi)始。與之相類似的就是數(shù)據(jù)庫(kù)的事務(wù)回滾,或者重做日志redo log等。
備忘錄模式(Memento),在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存著這個(gè)狀態(tài)。這樣以后就可將該對(duì)象恢復(fù)到原先保存的狀態(tài)。UML結(jié)構(gòu)圖如下:
其中,Originator是發(fā)起人,負(fù)責(zé)創(chuàng)建一個(gè)備忘錄Memento,用以記錄當(dāng)前時(shí)刻它的內(nèi)部狀態(tài),并可使用備忘錄恢復(fù)內(nèi)部狀態(tài);Memento是備忘錄,負(fù)責(zé)存儲(chǔ)Originator對(duì)象的內(nèi)部狀態(tài),并可防止Originator以外的其他對(duì)象訪問(wèn)備忘錄Memento;Caretaker是管理者,負(fù)責(zé)保存好備忘錄的Memento,不能對(duì)備忘錄的內(nèi)容進(jìn)行操作或檢查。
記錄當(dāng)前時(shí)刻的內(nèi)部狀態(tài),并負(fù)責(zé)創(chuàng)建和恢復(fù)備忘錄數(shù)據(jù),允許訪問(wèn)返回到先前狀態(tài)所需的所有數(shù)據(jù)。
1 public class Originator { 2 3 private String state; 4 5 public String getState() { 6 return state; 7 } 8 9 public void setState(String state) {10 this.state = state;11 }12 13 public Memento createMento() {14 return (new Memento(state));15 }16 17 public void setMemento(Memento memento) {18 state = memento.getState();19 }20 21 public void show() {22 System.out.println("state = " + state);23 }24 25 }
負(fù)責(zé)存儲(chǔ)Originator發(fā)起人對(duì)象的內(nèi)部狀態(tài),在需要的時(shí)候提供發(fā)起人需要的內(nèi)部狀態(tài)。
1 public class Memento { 2 3 private String state; 4 5 public Memento(String state) { 6 this.state = state; 7 } 8 9 public String getState() {10 return state;11 }12 13 }
對(duì)備忘錄進(jìn)行管理、保存和提供備忘錄,只能將備忘錄傳遞給其他角色。
1 public class Caretaker { 2 3 private Memento memento; 4 5 public Memento getMemento() { 6 return memento; 7 } 8 9 public void setMemento(Memento memento) {10 this.memento = memento;11 }12 13 }
下面編寫一小段代碼測(cè)試一下,即先將狀態(tài)置為On,保存后再將狀態(tài)置為Off,然后通過(guò)備忘錄管理員角色恢復(fù)初始狀態(tài)。
1 public class Client { 2 3 public static void main(String[] args) { 4 Originator originator = new Originator(); 5 originator.setState("On"); //Originator初始狀態(tài) 6 originator.show(); 7 8 Caretaker caretaker = new Caretaker(); 9 caretaker.setMemento(originator.createMento());10 11 originator.setState("Off"); //Originator狀態(tài)變?yōu)镺ff12 originator.show();13 14 originator.setMemento(caretaker.getMemento()); //回復(fù)初始狀態(tài)15 originator.show();16 }17 18 }
運(yùn)行結(jié)果如下:
需要記錄一個(gè)對(duì)象的內(nèi)部狀態(tài)時(shí),為了允許用戶取消不確定或者錯(cuò)誤的操作,能夠恢復(fù)到原先的狀態(tài)
通過(guò)一個(gè)備忘錄類專門存儲(chǔ)對(duì)象狀態(tài)
給用戶提供了一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用能夠比較方便地回到某個(gè)歷史的狀態(tài)
實(shí)現(xiàn)了信息的封裝,使得用戶不需要關(guān)心狀態(tài)的保存細(xì)節(jié)
消耗資源
需要保存和恢復(fù)數(shù)據(jù)的相關(guān)場(chǎng)景
提供一個(gè)可回滾的操作,如ctrl+z、瀏覽器回退按鈕、Backspace鍵等
需要監(jiān)控的副本場(chǎng)景
游戲存檔
ctrl+z鍵、瀏覽器回退鍵等(撤銷/還原)
棋盤類游戲的悔棋
數(shù)據(jù)庫(kù)事務(wù)的回滾
為了符合迪米特法則,需要有一個(gè)管理備忘錄的類
不要在頻繁建立備份的場(chǎng)景中使用備忘錄模式。為了節(jié)約內(nèi)存,可使用原型模式+備忘錄模式
下面以游戲存檔為例,看一下如何用備忘錄模式實(shí)現(xiàn)。UML圖如下:
簡(jiǎn)單記錄了游戲角色的生命力、攻擊力、防御力,通過(guò)saveState()方法來(lái)保存當(dāng)前狀態(tài),通過(guò)recoveryState()方法來(lái)恢復(fù)角色狀態(tài)。
1 public class GameRole { 2 3 private int vit; //生命力 4 private int atk; //攻擊力 5 private int def; //防御力 6 7 public int getVit() { 8 return vit; 9 }10 public void setVit(int vit) {11 this.vit = vit;12 }13 public int getAtk() {14 return atk;15 }16 public void setAtk(int atk) {17 this.atk = atk;18 }19 public int getDef() {20 return def;21 }22 public void setDef(int def) {23 this.def = def;24 }25 26 //狀態(tài)顯示27 public void stateDisplay() {28 System.out.println("角色當(dāng)前狀態(tài):");29 System.out.println("體力:" + this.vit);30 System.out.println("攻擊力:" + this.atk);31 System.out.println("防御力: " + this.def);32 System.out.println("-----------------");33 }34 35 //獲得初始狀態(tài)36 public void getInitState() {37 this.vit = 100;38 this.atk = 100;39 this.def = 100;40 }41 42 //戰(zhàn)斗后43 public void fight() {44 this.vit = 0;45 this.atk = 0;46 this.def = 0;47 }48 49 //保存角色狀態(tài)50 public RoleStateMemento saveState() {51 return (new RoleStateMemento(vit, atk, def));52 }53 54 //恢復(fù)角色狀態(tài)55 public void recoveryState(RoleStateMemento memento) {56 this.vit = memento.getVit();57 this.atk = memento.getAtk();58 this.def = memento.getDef();59 }60 61 }
備忘錄類,用于存儲(chǔ)角色狀態(tài)。
1 public class RoleStateMemento { 2 3 private int vit; //生命力 4 private int atk; //攻擊力 5 private int def; //防御力 6 7 public RoleStateMemento(int vit, int atk, int def) { 8 this.vit = vit; 9 this.atk = atk;10 this.def = def;11 }12 13 public int getVit() {14 return vit;15 }16 17 public void setVit(int vit) {18 this.vit = vit;19 }20 21 public int getAtk() {22 return atk;23 }24 25 public void setAtk(int atk) {26 this.atk = atk;27 }28 29 public int getDef() {30 return def;31 }32 33 public void setDef(int def) {34 this.def = def;35 }36 37 }
備忘錄管理者。
1 public class RoleStateCaretaker { 2 3 private RoleStateMemento memento; 4 5 public RoleStateMemento getMemento() { 6 return memento; 7 } 8 9 public void setMemento(RoleStateMemento memento) {10 this.memento = memento;11 }12 13 }
下面編寫一個(gè)簡(jiǎn)單的程序測(cè)試一下,編寫邏輯大致為打boss前存檔,打boss失敗了,讀檔。
1 public class Client { 2 3 public static void main(String[] args) { 4 //打boss前 5 GameRole gameRole = new GameRole(); 6 gameRole.getInitState(); 7 gameRole.stateDisplay(); 8 9 //保存進(jìn)度10 RoleStateCaretaker caretaker = new RoleStateCaretaker();11 caretaker.setMemento(gameRole.saveState());12 13 //打boss失敗14 gameRole.fight();15 gameRole.stateDisplay();16 17 //恢復(fù)狀態(tài)18 gameRole.recoveryState(caretaker.getMemento());19 gameRole.stateDisplay();20 }21 22 }
運(yùn)行結(jié)果如下:
聯(lián)系客服