問題引入,把‘游戲角色’的存取狀態細節封裝起來,而且最好是封裝在外部的類當中,以體現職責分離。
備忘錄模式(Memento),在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該對象回復到原先保存的狀態。
備忘錄模式的結構圖
涉及角色:
1.Originator(發起人):負責創建一個備忘錄Memento,用以記錄當前時刻自身的內部狀態,並可使用備忘錄恢復內部狀態。Originator可以根據需要決定Memento存儲自己的哪些內部狀態。
2.Memento(備忘錄):負責存儲Originator對象的內部狀態,並可以防止Originator以外的其他對象訪問備忘錄。備忘錄有兩個接口:Caretaker只能看到備忘錄的窄接口,他只能將備忘錄傳遞給其他對象。Originator卻可看到備忘錄的寬接口,允許它訪問返回到先前狀態所需要的所有數據。
3.Caretaker(管理者):負責備忘錄Memento,不能對Memento的內容進行訪問或者操作。
備忘錄模式的優點和缺點
一、備忘錄模式的優點
1、有時一些發起人對象的內部信息必須保存在發起人對象以外的地方,但是必須要由發起人對象自己讀取,這時,
使用備忘錄模式可以把復雜的發起人內部信息對其他的對象屏蔽起來,從而可以恰當地保持封裝的邊界。
2、本模式簡化了發起人類。發起人不再需要管理和保存其內部狀態的一個個版本,客戶端可以自行管理他們所需
要的這些狀態的版本。
3、當發起人角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時存儲起來的備忘錄將狀態復原。
二、備忘錄模式的缺點:
1、如果發起人角色的狀態需要完整地存儲到備忘錄對象中,那麼在資源消耗上面備忘錄對象會很昂貴。
2、當負責人角色將一個備忘錄 存儲起來的時候,負責人可能並不知道這個狀態會占用多大的存儲空間,從而無法提醒用戶一個操作是否很昂貴。
3、當發起人角色的狀態改變的時候,有可能這個協議無效。如果狀態改變的成功率不高的話,不如采取“假如”協議模式。
通用代碼實現
class Originator {
//需要保存的屬性,可以是多個
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//創建備忘錄,將當前需要保存的信息導入並實例化出一個Memento對象
public Memento createMemento(){
return new Memento(this.state);
}
//回復備忘錄,將Memento導入並將相關數據恢復
public void restoreMemento(Memento memento){
this.setState(memento.getState());
}
}
class Memento {
private String state = "";
public Memento(String state){
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
class Caretaker {
private Memento memento;
public Memento getMemento(){
return memento;
}
public void setMemento(Memento memento){
this.memento = memento;
}
}
public class Client {
public static void main(String[] args){
Originator originator = new Originator();
originator.setState("狀態1");
System.out.println("初始狀態:"+originator.getState());
Caretaker caretaker = new Caretaker();
caretaker.setMemento(originator.createMemento());
originator.setState("狀態2");
System.out.println("改變後狀態:"+originator.getState());
originator.restoreMemento(caretaker.getMemento());
System.out.println("恢復後狀態:"+originator.getState());
}
}
代碼演示了一個單狀態單備份的例子,邏輯非常簡單:Originator類中的state變量需要備份,以便在需要的時候恢復;Memento類中,也有一個state變量,用來存儲Originator類中state變量的臨時狀態;而Caretaker類就是用來管理備忘錄類的,用來向備忘錄對象中寫入狀態或者取回狀態。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
舉例說明:
以保存游戲進度為例,在游戲角色大戰Boss前將該角色的狀態存儲,與Boss作戰後角色的各項能力會下降,如果沒有通關,則可利用備忘錄進行恢復到戰前狀態。
游戲角色類:
package com.zyh.designpattern.memento;
public class PlayRole {
private int vitality;
private int aggressivity;
private int defencivity;
public PlayRole(int vitality, int aggressivity, int defencivity) {
super();
this.vitality = vitality;
this.aggressivity = aggressivity;
this.defencivity = defencivity;
}
public PlayRole() {}
public int getVitality() {
return vitality;
}
public void setVitality(int vitality) {
this.vitality = vitality;
}
public int getAggressivity() {
return aggressivity;
}
public void setAggressivity(int aggressivity) {
this.aggressivity = aggressivity;
}
public int getDefencivity() {
return defencivity;
}
public void setDefencivity(int defencivity) {
this.defencivity = defencivity;
}
//增加“保存角色狀態的方法”,將游戲角色的三個狀態值通過實例化“角色狀態存儲箱”返回
public RoleMemento createMemento() {
RoleMemento memento = new RoleMemento();
memento.setAggressivity(aggressivity);
memento.setDefencivity(defencivity);
memento.setVitality(vitality);
return memento;
}
//新增“恢復角色狀態”方法,可將外部的“角色狀態存儲箱”中狀態值恢復給游戲角色
public void setMemento(RoleMemento memento) {
this.aggressivity = memento.getAggressivity();
this.defencivity = memento.getDefencivity();
this.vitality = memento.getVitality();
}
public void showState() {
System.out.println("攻擊力:" + this.aggressivity + "|防御力:" + this.defencivity
+ "|生命力:" + this.vitality);
}
}
備忘錄類:
package com.zyh.designpattern.memento;
public class RoleMemento {
private int vitality;
private int aggressivity;
private int defencivity;
public int getVitality() {
return vitality;
}
public void setVitality(int vitality) {
this.vitality = vitality;
}
public int getAggressivity() {
return aggressivity;
}
public void setAggressivity(int aggressivity) {
this.aggressivity = aggressivity;
}
public int getDefencivity() {
return defencivity;
}
public void setDefencivity(int defencivity) {
this.defencivity = defencivity;
}
}
管理者類
package com.zyh.designpattern.memento;
public class Caretaker {
RoleMemento memento;
public RoleMemento getMemento() {
return memento;
}
public void setMemento(RoleMemento memento) {
this.memento = memento;
}
}
客戶端:
package com.zyh.designpattern.memento;
public class Client {
public static void main(String[] args) {
// 測試程序
// 新建角色
PlayRole role = new PlayRole(100, 100, 100);
// 新建管理者
Caretaker taker = new Caretaker();
// 角色初始狀態
System.out.println("游戲剛開始,角色各屬性:");
role.showState();
// 利用備忘錄模式保存當前狀態
System.out.println("\n【保存游戲狀態!】\n");
taker.setMemento(role.createMemento());
role.setAggressivity(20);
role.setDefencivity(30);
role.setVitality(0);
// 大戰過後,角色能力值下降
System.out.println("與Boss對戰後,角色各項能力已大大下降:");
role.showState();
// 恢復保存的角色狀態
role.setMemento(taker.getMemento());
System.out.println("\n【恢復保存的角色狀態!】");
System.out.println("\n恢復後角色的當前狀態:");
role.showState();
}
}