(十六)備忘錄模式
1 概述
備忘錄(Memento)模式:在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到原先儲存的狀態。
-
Originator(發起人):負責建立一個備忘錄 Memento,用以記錄當前時刻它的內部狀態,並可使用備忘錄恢復內部狀態。Originator 可根據需要決定 Memento 儲存 Originator 的哪些內部狀態。
-
Memento(備忘錄):負責儲存 Originator 物件的內部狀態,並可防止 Originator 以外的其他物件訪問備忘錄 Memento。備忘錄有兩個介面,Caretaker 只能看到備忘錄的窄介面,它只能將備忘錄傳遞給其他物件。Originator 能夠看到一個寬介面,允許它訪問返回到先前狀態所需的所有資料。
-
Caretaker(管理者):負責儲存好備忘錄 Memento,不能對備忘錄的內容進行操作或檢查。
把要儲存的細節給封裝在 Memento 中,要更改儲存的細節也不用影響客戶端。
2 使用條件
-
Memento 模式比較適用於功能比較複雜的,但需要維護或記錄屬性歷史的類,或者需要儲存的屬性只是眾多屬性中的一小部分時,Originator 可以根據儲存的 Memento 資訊還原到前一狀態。
-
如果在某個系統中使用命令模式時,需要實現命令的撤銷功能,那麼命令模式可以使用備忘錄模式來儲存可撤銷操作的狀態。
-
有時一些物件的內部資訊必須儲存在物件以外的地方,但是必須要由物件自己讀取,這時,使用備忘錄可以把複雜的物件內部資訊對其他的物件遮蔽起來,從而可以恰當地保持封裝的邊界。
-
當角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時儲存起來的備忘錄將狀態復原。
3 示例
遊戲進度備忘:模擬遊戲角色的存檔與恢復功能。
遊戲角色類
public class GameRole { private int hp; // 生命 private int atk; // 攻擊 private int def; // 防禦 // “儲存角色狀態”方法,將遊戲角色的三個狀態值通過例項化“角色狀態儲存箱”返回 public RoleStateMemento saveState() { return new RoleStateMemento(hp, atk, def); } // “恢復角色狀態”方法,可將外部的“角色狀態儲存箱”中狀態值恢復給遊戲角色 public void recoveryState(RoleStateMemento memento) { this.hp = memento.getHp(); this.atk = memento.getAtk(); this.def = memento.getDef(); } // 初始化狀態 public void initState() { this.hp = 100; this.atk = 100; this.def = 100; } // 戰鬥 public void fight() { this.hp = 0; } // 狀態顯示 public void stateDisplay() { System.out.println("當前角色狀態:"); System.out.println("生命值:" + this.hp); System.out.println("攻擊力:" + this.atk); System.out.println("防禦力:" + this.def); } public int getHp() { return hp; } public void setHp(int hp) { this.hp = hp; } 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; } }
角色狀態儲存箱類
public class RoleStateMemento {
private int hp; // 生命
private int atk; // 攻擊
private int def; // 防禦
// 將生命力、攻擊力、防禦力存入狀態儲存箱物件中
public RoleStateMemento(int hp, int atk, int def) {
this.hp = hp;
this.atk = atk;
this.def = def;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
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;
}
}
角色狀態管理者類
public class RoleStateCaretaker {
private RoleStateMemento memento;
public RoleStateMemento getMemento() {
return memento;
}
public void setMemento(RoleStateMemento memento) {
this.memento = memento;
}
}
客戶端程式碼
public class Client {
public static void main(String[] args) {
// Boss戰前
// 遊戲角色初始狀態,三項指標資料都是100
GameRole role = new GameRole();
role.initState();
role.stateDisplay();
// 儲存進度
// 儲存進度時,由於封裝在Memento中,因此我們並不知道儲存了哪些具體的角色資料
RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
stateAdmin.setMemento(role.saveState());
// 大戰Boss時,損耗嚴重
// 開始打Boss,非常糟糕,GameOver了
role.fight();
role.stateDisplay();
// 恢復之前狀態
role.recoveryState(stateAdmin.getMemento());
role.stateDisplay();
}
}
輸出入下
當前角色狀態:
生命值:100
攻擊力:100
防禦力:100
當前角色狀態:
生命值:0
攻擊力:100
防禦力:100
當前角色狀態:
生命值:100
攻擊力:100
防禦力:100
Process finished with exit code 0
參考書籍:
《大話設計模式》