1. 程式人生 > 其它 >(十六)備忘錄模式

(十六)備忘錄模式

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

參考書籍:
《大話設計模式》