舉個慄子 問題描述 打游戲存進度。 簡單實現 GameRole 測試 測試結果 存在問題 在客戶端調用這段,把整個游戲角色的細節暴露了,職責太大,需要知道游戲角色的生命力、攻擊力、防禦力這些細節,還要進行備份。如果以後需要增加“魔法力”或修改現有的某種力,那這部分代碼就需要修改,同樣恢復時也是一樣的 ...
舉個慄子
問題描述
打游戲存進度。
簡單實現
GameRole
/**
* 游戲角色
* Created by callmeDevil on 2019/8/11.
*/
public class GameRole {
// 生命力
private int vit;
// 攻擊力
private int atk;
// 防禦力
private int def;
// 狀態顯示
public void stateDisplay() {
System.out.println("角色當前狀態:");
System.out.println(String.format("體力:%s", this.vit));
System.out.println(String.format("攻擊力:%s", this.atk));
System.out.println(String.format("防禦力:%s", this.def));
System.out.println();
}
// 獲得初始狀態
public void getInitState() {
// 數據通常來自本機磁碟或遠程資料庫
this.vit = 100;
this.atk = 100;
this.def = 100;
}
// 戰鬥
public void fight(){
// 在與Boss大戰後游戲數據損耗為0
this.vit = 0;
this.atk = 0;
this.def = 0;
}
// 省略 get set
}
測試
public class Test {
public static void main(String[] args) {
// 大戰Boss前
GameRole lufi = new GameRole();
// 獲得初始角色狀態
lufi.getInitState();
lufi.stateDisplay();
// 通過“游戲角色”新實例,保存進度
GameRole backup = new GameRole();
backup.setVit(lufi.getVit());
backup.setAtk(lufi.getAtk());
backup.setDef(lufi.getDef());
// 大戰Boss時,損耗嚴重,全部為0
lufi.fight();
lufi.stateDisplay();
// GameOver不甘心,恢復進度,重新玩過
lufi.setVit(backup.getVit());
lufi.setAtk(backup.getAtk());
lufi.setDef(backup.getDef());
lufi.stateDisplay();
}
}
測試結果
角色當前狀態:
體力:100
攻擊力:100
防禦力:100
角色當前狀態:
體力:0
攻擊力:0
防禦力:0
角色當前狀態:
體力:100
攻擊力:100
防禦力:100
存在問題
在客戶端調用這段,把整個游戲角色的細節暴露了,職責太大,需要知道游戲角色的生命力、攻擊力、防禦力這些細節,還要進行備份。如果以後需要增加“魔法力”或修改現有的某種力,那這部分代碼就需要修改,同樣恢復時也是一樣的問題。
備忘錄模式
定義
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。
UML圖
代碼實現
GameRole
/**
* 游戲角色
* Created by callmeDevil on 2019/8/11.
*/
public class GameRole {
// 屬性與簡單實現GameRole相同
// 保存角色狀態
public RoleStateMemento saveState() {
return new RoleStateMemento(vit, atk, def);
}
// 恢復角色狀態
public void recoveryState(RoleStateMemento memento) {
this.vit = memento.getVit();
this.atk = memento.getAtk();
this.def = memento.getDef();
}
// 其餘方法與簡單實現相同
}
RoleStateMemento
/**
* 角色狀態存儲類
* Created by callmeDevil on 2019/8/11.
*/
public class RoleStateMemento {
// 屬性與 簡單實現 GameRole 相同
// 將生命力、攻擊力、防禦力存入狀態存儲箱對象中
public RoleStateMemento(int vit, int atk, int def){
this.vit = vit;
this.atk = atk;
this.def = def;
}
// 省略 get set
}
RoleStateCaretaker
/**
* 游戲狀態管理者
* Created by callmeDevil on 2019/8/11.
*/
public class RoleStateCaretaker {
private RoleStateMemento memento;
// 省略 get set
}
測試
public class Test {
public static void main(String[] args) {
// 大戰Boss前
GameRole lufi = new GameRole();
lufi.getInitState();
lufi.stateDisplay();
// 保存游戲進度
RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
stateAdmin.setMemento(lufi.saveState());// 將具體數據封裝在了 Memento中
// 大戰Boss時,損耗嚴重
lufi.fight();
lufi.stateDisplay();
// 恢復狀態
lufi.recoveryState(stateAdmin.getMemento());
lufi.stateDisplay();
}
}
測試結果
與簡單實現相同
總結
- 把要保存的細節給封裝在了 Memento 中,哪一天要更改保存細節也不用影響客戶端。
- 備忘錄模式比較適用於功能比較複雜的,但需要維護或記錄屬性歷史的類,或者需要保存的屬性只是眾多屬性的一小部分時,Originator 可以根據保存的 Memento 信息還原到前一狀態。
- 如果在某個系統中使用命令模式時,需要實現命令的撤銷功能,那麼命令模式可以使用備忘錄模式來存儲可撤銷操作的狀態。
- 使用備忘錄可以把複雜的對象內部信息對其他的對象屏蔽起來。