java/android 設計模式學習筆記(21)---備忘錄模式

  這篇博客我們來介紹一下備忘錄模式(Memento Pattern),也是行爲型模式設計模式之一,備忘錄模式又稱爲快照(Snapshot Pattern)模式或者 Token 模式,該模式用於保存對象當前狀態,並且在之後可以再次恢復到此狀態。備忘錄模式實現的方式需要保證被保存的對象狀態不能被對象從外部訪問(an opaque object),目的是爲了保護被保存的這些對象狀態的完整性以及內部實現不向外暴露。
  轉載請註明出處:http://blog.csdn.net/self_study/article/details/52561728
  PS:對技術感興趣的同鞋加羣544645972一起交流。

設計模式總目錄

  java/android 設計模式學習筆記目錄

特點

  在不破壞封裝的前提下,捕捉一個對象的內部狀態,並在該對象之外保存這個狀態,這樣,以後就可將該對象恢復到原先保存的狀態。
  備忘錄模式使用的場景:

  • 需要保存一個對象在某一個時刻的狀態或部分狀態;
  • 如果用一個接口來讓其他對象得到這些狀態,將會暴露對象的實現細節並破壞對象的封裝性,一個對象不希望外界直接訪問其內部狀態,通過中間對象可以簡潔訪問其內部狀態。

UML類圖

  來看看備忘錄模式的 uml 類圖:
  這裏寫圖片描述
備忘錄模式有三個角色:

  • Originator:負責創建一個備忘錄,可以記錄、恢復自身的內部狀態,同時 Originator 還可以根據需要決定 Memento 存儲自身的哪些內部狀態。
  • Memento:備忘錄角色,用於存儲 Originator 的內部狀態,並且可以防止 Originator 以外的對象訪問 Memento。
  • CareTaker:負責存儲備忘錄,不能對備忘錄的內容進行操作和訪問,只能夠將備忘錄傳遞給其他對象。
  我們這裏可以寫出通用代碼:
Originator.class

public class Originator {
    private int state = 0;

    public void setState(int state) {
        this.state = state;
    }

    public void print() {
        System.out.print("state = " + state + "\n");
    }

    public void restore(Memento memento) {
        setState(memento.getState());
    }

    public Memento createMemoto() {
        Memento memento = new Memento();
        memento.setState(state);
        return memento;
    }

    public static void main(String args[]) {
        Originator originator = new Originator();
        originator.setState(1);
        Caretaker caretaker = new Caretaker();
        caretaker.storeMemento(originator.createMemoto());
        System.out.print("before\n");
        originator.print();
        originator.setState(2);
        System.out.print("after\n");
        originator.print();
        originator.restore(caretaker.restoreMemento());
        System.out.print("restore to the original state\n");
        originator.print();
    }
}

Memento.class

public class Memento {
    private int mState = 0;

    public void setState(int state) {
        this.mState = state;
    }

    public int getState() {
        return mState;
    }
}

Caretaker.class

public class Caretaker {
    private Memento memento;

    public Memento restoreMemento() {
        return memento;
    }

    public void storeMemento(Memento memento) {
        this.memento = memento;
    }
}

最後輸出:

before
state = 1
after
state = 2
restore to the original state
state = 1

  上面的實現方式中備忘錄角色的內部所存儲的狀態對所有對象公開,因此很多時候這個被稱爲“白箱”備忘錄模式,它將發起人角色的狀態存儲在一個大家都看得到的地方,因此是破壞封裝性的,但是通過程序員自律,同樣可以一定程度上實現模式的大部分用意,所以它仍然是有意義的;“黑箱”備忘錄模式和白箱備忘錄模式的不同之處是 Memento 設計成了 Originator 的內部類,從而將 Memento 的對象封裝在 Originator 裏面,在外面提供一個標識接口 MementoIF 給 Caretaker 以及其他對象,並且讓 Memento 實現 MementoIF 接口,或者不用 MementoIF 接口,直接將 Memento 類設計成靜態內部類也是可以的。
這裏寫圖片描述

示例與源碼

  在 Android 源碼中,我們經常會用到的 onSaveInstanceState 和 onRestoreInstanceState 就是一個備忘錄模式,在這個過程中,Activity 扮演了Caretaker 的角色,負責存儲、恢復 UI 的狀態信息;Activity、Fragment、View、ViewGroup 等對象爲 Originator 角色,也就是需要存儲狀態的對象;Memento 則是由 Bundle 類扮演。Activity 在停止之前會根據 Activity 的退出情景來選擇是否需要存儲狀態,在重新啓動該 Activity 時會判斷 ActivityClientRecord 對象中是否存儲了 Activity 的狀態,如果含有狀態則調用 Activity 的 onRestoreInstanceState 函數,從而使得 Activity 的 UI 效果和上次保持一致,這樣一來,就保證了在非正常情況退出 Activity 時不會丟失數據的情況,很好的提升了用戶體驗。
  我們這裏以 wiki 上的”黑箱”備忘錄模式爲例,看一下和”白箱”備忘錄模式的區別:
Originator.class

class Originator {
    private String state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento..

    public void set(String state) {
        System.out.println("Originator: Setting state to " + state);
        this.state = state;
    }

    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(this.state);
    }

    public void restoreFromMemento(Memento memento) {
        this.state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }

    public static class Memento {
        private final String state;

        public Memento(String stateToSave) {
            state = stateToSave;
        }

        private String getSavedState() {
            return state;
        }
    }
}

Caretaker.class

public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();

        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.add(originator.saveToMemento());
        originator.set("State4");

        originator.restoreFromMemento(savedStates.get(1));   
    }

輸出:

Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3

將 Memento 類設置成爲 Originator 的靜態內部類之後,Memento 的內部實現細節就可以只對 Originator 暴露了,實現了”黑箱”的封裝性,我們這裏省掉了 Caretaker 和 MementoIF 這兩個角色,根據需要也可以去實現這兩個角色。

總結

  備忘錄模式是在不破壞封裝的條件下,通過備忘錄對象(Memento)存儲另一個對象內部狀態的快照,在將來合適的時候把這個對象還原到存儲起來的狀態。

  • 給用戶提供了一種可以恢復狀態的機制,可以使用戶能夠比較方便地回到某個歷史的狀態;
  • 實現了信息的封裝,維護內聚,使得用戶不需要關心狀態的保存細節。
  缺點:消耗資源,如果類的成員變量過多,勢必會佔用比較大的資源,而且每一次保存都會消耗一定的內存,恢復狀態的過程也可能會很耗時。

源碼下載

  https://github.com/zhaozepeng/Design-Patterns/tree/master/MementoPattern

引用

https://en.wikipedia.org/wiki/Memento_pattern
http://www.cnblogs.com/java-my-life/archive/2012/06/06/2534942.html

發佈了92 篇原創文章 · 獲贊 412 · 訪問量 70萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章