1. 程式人生 > >Java設計模式之從[暗黑破壞神存檔點]分析備忘錄(Memento)模式

Java設計模式之從[暗黑破壞神存檔點]分析備忘錄(Memento)模式

  在大部分遊戲中,都有一個“存檔點”的概念。例如,在挑戰boss前,遊戲會在某個地方存檔,如果玩家挑戰boss失敗,則會從這個存檔點開始重新遊戲。因此,我們可以將這個“存檔點”當成是一個備忘錄,我們將此時玩家所有的狀態儲存下來,以便之後的讀取。

  備忘錄模式正是如此,它在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可以將該物件恢復到原先的儲存狀態了。

  一個簡單的例子,假設玩家在暗黑破壞神中準備挑戰Boss巴爾,玩家攜帶了10瓶藥劑,並且在巴爾的門前“世界之石”存了檔,玩家在挑戰Boss時使用了1瓶藥劑,後來挑戰失敗了,於是他重新讀檔,再次回到了世界之石,此時他身上攜帶的藥劑數量仍然應該為10瓶。

  具體的程式碼如下:

interface IMemento{
}

class CharacterOriginator{
    private int potion;
    private String position;
    public void move(String place){
        position = place;
    }
    public void usePotion(){
        potion --;
    }
    public int getPotion() {
        return potion;
    }
    private void setPotion(int potion) {
        this.potion = potion;
    }
    public String getPosition() {
        return position;
    }
    private void setPosition(String position) {
        this.position = position;
    }
    public IMemento createMemento(){
        return new Memento(potion, position);
    }
    public void restoreMemento(IMemento memento){
        setPotion(((Memento)memento).getPotion());
        setPosition(((Memento)memento).getPosition());
    }
    public CharacterOriginator(int potion, String position){
        this.potion = potion;
        this.position = position;
    }
    private class Memento implements IMemento{
        private int potion;
        String position;
        public Memento(int potion, String position){
            this.potion = potion;
            this.position = position;
        }
        public int getPotion() {
            return potion;
        }
        public String getPosition() {
            return position;
        }
        
    }
}

class Caretaker{
    IMemento memento;
    public IMemento getMemento() {
        return memento;
    }
    public void setMemento(IMemento memento) {
        this.memento = memento;
    }
}


class Memento
{
    public static void main(String[] args) {
        CharacterOriginator player = new CharacterOriginator(10, "世界之石");
        Caretaker caretaker = new Caretaker();
        caretaker.setMemento(player.createMemento());
        System.out.printf("玩家攜帶的藥劑:%d,所在位置:%s\n", player.getPotion(), player.getPosition());
        player.usePotion();
        player.move("巴爾的宮殿");
        //玩家挑戰失敗後重新讀檔
        System.out.printf("玩家攜帶的藥劑:%d,所在位置:%s\n", player.getPotion(), player.getPosition());
        player.restoreMemento(caretaker.getMemento());
        System.out.printf("玩家攜帶的藥劑:%d,所在位置:%s\n", player.getPotion(), player.getPosition());
    }

  現在來分析一下上面的程式碼,並同時講解備忘錄模式。備忘錄模式有3個參與者:Originator——負責建立備忘錄、還原備忘錄,人物的各種狀態都在此,如例子中的CharacterOriginator。Memento——備忘錄,它表示需要儲存哪些資料,Originator通過需要在備忘錄上取資料。在上例中,Memento為Originator的一個內部類。Caretaker——負責儲存、獲取Memento。為了不暴露Memento的內部方法(我們認為,Memento的內部方法只能由CharacterOriginator獲取),我們讓Memento繼承了一個空介面IMemento,因此通過Caretaker獲得的備忘錄均是IMemento型別的,我們無法對它做任何事情,但是可以把它傳給CharacterOriginator,然後Originator的restoreMemento可以把IMemento型別強制轉換為Memento型別。請注意,由於Memento為內部私有類,因此除了CharacterOriginator外,其餘的類無法呼叫Memento的方法,這也就是我們說的“不暴露Memento內部方法”。restoreMemento中清晰寫明瞭應該還原哪些狀態,在這裡就不進行詳細解釋了。

  最後我們看看main方法。首先例項化了一個玩家(player),他有10瓶藥水,位於世界之石;隨後他進入了巴爾的宮殿,並用掉了一瓶藥水;最後,他由於挑戰失敗而恢復到了以前的存檔點——擁有10瓶藥水,位於世界之石。因此,程式執行的結果為:

玩家攜帶的藥劑:10,所在位置:世界之石

玩家攜帶的藥劑:9,所在位置:巴爾的宮殿

玩家攜帶的藥劑:10,所在位置:世界之石

  以上就是備忘錄模式的說明,希望能對大家有所幫助。