一、概述
備忘錄模式提供了一種狀態(tài)恢復(fù)的實(shí)現(xiàn)機(jī)制,使得用戶可以方便地回到一個(gè)特定的歷史步驟,當(dāng)新的狀態(tài)無效或者存在問題時(shí),可以使用暫時(shí)存儲(chǔ)起來的備忘錄將狀態(tài)復(fù)原,很多軟件都提供了撤銷(Undo)操作,如 Word、記事本、Photoshop、IDEA等軟件在編輯時(shí)按 Ctrl+Z 組合鍵時(shí)能撤銷當(dāng)前操作,使文檔恢復(fù)到之前的狀態(tài);還有在 瀏覽器 中的后退鍵、數(shù)據(jù)庫事務(wù)管理中的回滾操作、玩游戲時(shí)的中間結(jié)果存檔功能、數(shù)據(jù)庫與操作系統(tǒng)的備份操作、棋類游戲中的悔棋功能等都屬于這類。
定義:
又叫快照模式,在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),以便以后當(dāng)需要時(shí)能將該對(duì)象恢復(fù)到原先保存的狀態(tài)。
二、結(jié)構(gòu)
備忘錄模式的主要角色如下:
- 發(fā)起人(Originator)角色:記錄當(dāng)前時(shí)刻的內(nèi)部狀態(tài)信息,提供創(chuàng)建備忘錄和恢復(fù)備忘錄數(shù)據(jù)的功能,實(shí)現(xiàn)其他業(yè)務(wù)功能,它可以訪問備忘錄里的所有信息。
- 備忘錄(Memento)角色:負(fù)責(zé)存儲(chǔ)發(fā)起人的內(nèi)部狀態(tài),在需要的時(shí)候提供這些內(nèi)部狀態(tài)給發(fā)起人。
- 管理者(Caretaker)角色:對(duì)備忘錄進(jìn)行管理,提供保存與獲取備忘錄的功能,但其不能對(duì)備忘錄的內(nèi)容進(jìn)行訪問與修改。
備忘錄有兩個(gè)等效的接口:
- 窄接口:管理者(Caretaker)對(duì)象(和其他發(fā)起人對(duì)象之外的任何對(duì)象)看到的是備忘錄的窄接口(narror Interface),這個(gè)窄接口只允許他把備忘錄對(duì)象傳給其他的對(duì)象。
- 寬接口:與管理者看到的窄接口相反,發(fā)起人對(duì)象可以看到一個(gè)寬接口(wide Interface),這個(gè)寬接口允許它讀取所有的數(shù)據(jù),以便根據(jù)這些數(shù)據(jù)恢復(fù)這個(gè)發(fā)起人對(duì)象的內(nèi)部狀態(tài)。
三、案例實(shí)現(xiàn)
【例】游戲挑戰(zhàn)BOSS
游戲中的某個(gè)場(chǎng)景,一游戲角色有生命力、攻擊力、防御力等數(shù)據(jù),在打Boss前和后一定會(huì)不一樣的,我們?cè)试S玩家如果感覺與Boss決斗的效果不理想可以讓游戲恢復(fù)到?jīng)Q斗之前的狀態(tài)。
要實(shí)現(xiàn)上述案例,有兩種方式:
- “白箱”備忘錄模式
- “黑箱”備忘錄模式
(一)“白箱”備忘錄模式
備忘錄角色對(duì)任何對(duì)象都提供一個(gè)接口,即寬接口,備忘錄角色的內(nèi)部所存儲(chǔ)的狀態(tài)就對(duì)所有對(duì)象公開。類圖如下:
代碼如下:
//游戲角色類
public class GameRole {
private int vit; //生命力
private int atk; //攻擊力
private int def; //防御力
//初始化狀態(tài)
public void initState() {
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//戰(zhàn)斗
public void fight() {
this.vit = 0;
this.atk = 0;
this.def = 0;
}
//保存角色狀態(tài)
public RoleStateMemento saveState() {
return new RoleStateMemento(vit, atk, def);
}
//回復(fù)角色狀態(tài)
public void recoverState(RoleStateMemento roleStateMemento) {
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
public void stateDisplay() {
System.out.println("角色生命力:" + vit);
System.out.println("角色攻擊力:" + atk);
System.out.println("角色防御力:" + def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
//游戲狀態(tài)存儲(chǔ)類(備忘錄類)
public class RoleStateMemento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
//角色狀態(tài)管理者類
public class RoleStateCaretaker {
private RoleStateMemento roleStateMemento;
public RoleStateMemento getRoleStateMemento() {
return roleStateMemento;
}
public void setRoleStateMemento(RoleStateMemento roleStateMemento) {
this.roleStateMemento = roleStateMemento;
}
}
//測(cè)試類
public class Client {
public static void main(String[] args) {
System.out.println("------------大戰(zhàn)Boss前------------");
//大戰(zhàn)Boss前
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
//保存進(jìn)度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
System.out.println("------------大戰(zhàn)Boss后------------");
//大戰(zhàn)Boss時(shí),損耗嚴(yán)重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("------------恢復(fù)之前狀態(tài)------------");
//恢復(fù)之前狀態(tài)
gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
gameRole.stateDisplay();
}
}
分析:白箱備忘錄模式是破壞封裝性的。但是通過程序員自律,同樣可以在一定程度上實(shí)現(xiàn)模式的大部分用意。
(二)“黑箱”備忘錄模式
備忘錄角色對(duì)發(fā)起人對(duì)象提供一個(gè)寬接口,而為其他對(duì)象提供一個(gè)窄接口。在Java語言中,實(shí)現(xiàn)雙重接口的辦法就是將備忘錄類設(shè)計(jì)成發(fā)起人類的內(nèi)部成員類。
將 RoleStateMemento
設(shè)為 GameRole
的內(nèi)部類,從而將 RoleStateMemento
對(duì)象封裝在 GameRole
里面;在外面提供一個(gè)標(biāo)識(shí)接口 Memento
給 RoleStateCaretaker
及其他對(duì)象使用。這樣 GameRole
類看到的是 RoleStateMemento
所有的接口,而RoleStateCaretaker
及其他對(duì)象看到的僅僅是標(biāo)識(shí)接口 Memento
所暴露出來的接口,從而維護(hù)了封裝型。類圖如下:
代碼如下:
窄接口Memento
,這是一個(gè)標(biāo)識(shí)接口,因此沒有定義出任何的方法
public interface Memento {
}
定義發(fā)起人類 GameRole
,并在內(nèi)部定義備忘錄內(nèi)部類 RoleStateMemento
(該內(nèi)部類設(shè)置為私有的)
/游戲角色類
public class GameRole {
private int vit; //生命力
private int atk; //攻擊力
private int def; //防御力
//初始化狀態(tài)
public void initState() {
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//戰(zhàn)斗
public void fight() {
this.vit = 0;
this.atk = 0;
this.def = 0;
}
//保存角色狀態(tài)
public Memento saveState() {
return new RoleStateMemento(vit, atk, def);
}
//回復(fù)角色狀態(tài)
public void recoverState(Memento memento) {
RoleStateMemento roleStateMemento = (RoleStateMemento) memento;
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
public void stateDisplay() {
System.out.println("角色生命力:" + vit);
System.out.println("角色攻擊力:" + atk);
System.out.println("角色防御力:" + def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
private class RoleStateMemento implements Memento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
}
負(fù)責(zé)人角色類 RoleStateCaretaker
能夠得到的備忘錄對(duì)象是以 Memento
為接口的,由于這個(gè)接口僅僅是一個(gè)標(biāo)識(shí)接口,因此負(fù)責(zé)人角色不可能改變這個(gè)備忘錄對(duì)象的內(nèi)容
//角色狀態(tài)管理者類
public class RoleStateCaretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
客戶端測(cè)試類
public class Client {
public static void main(String[] args) {
System.out.println("------------大戰(zhàn)Boss前------------");
//大戰(zhàn)Boss前
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
//保存進(jìn)度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setMemento(gameRole.saveState());
System.out.println("------------大戰(zhàn)Boss后------------");
//大戰(zhàn)Boss時(shí),損耗嚴(yán)重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("------------恢復(fù)之前狀態(tài)------------");
//恢復(fù)之前狀態(tài)
gameRole.recoverState(roleStateCaretaker.getMemento());
gameRole.stateDisplay();
}
}
四、優(yōu)缺點(diǎn)
1,優(yōu)點(diǎn):
- 提供了一種可以恢復(fù)狀態(tài)的機(jī)制。當(dāng)用戶需要時(shí)能夠比較方便地將數(shù)據(jù)恢復(fù)到某個(gè)歷史的狀態(tài)。
- 實(shí)現(xiàn)了內(nèi)部狀態(tài)的封裝。除了創(chuàng)建它的發(fā)起人之外,其他對(duì)象都不能夠訪問這些狀態(tài)信息。
- 簡(jiǎn)化了發(fā)起人類。發(fā)起人不需要管理和保存其內(nèi)部狀態(tài)的各個(gè)備份,所有狀態(tài)信息都保存在備忘錄中,并由管理者進(jìn)行管理,這符合單一職責(zé)原則。
2,缺點(diǎn):
- 資源消耗大。如果要保存的內(nèi)部狀態(tài)信息過多或者特別頻繁,將會(huì)占用比較大的內(nèi)存資源。
五、使用場(chǎng)景
-
需要保存與恢復(fù)數(shù)據(jù)的場(chǎng)景,如玩游戲時(shí)的中間結(jié)果的存檔功能。文章來源:http://www.zghlxwxcb.cn/news/detail-810051.html
-
需要提供一個(gè)可回滾操作的場(chǎng)景,如 Word、記事本、Photoshop,idea等軟件在編輯時(shí)按 Ctrl+Z 組合鍵,還有數(shù)據(jù)庫中事務(wù)操作。文章來源地址http://www.zghlxwxcb.cn/news/detail-810051.html
到了這里,關(guān)于Java設(shè)計(jì)模式-備忘錄模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!