設計模式之備忘錄模式

0x01.定義與類型

  • 定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態,以便之後當須要時能將該對象恢復到原先保存的狀態。該模式又叫快照模式。
  • 類型:行爲型
  • UML類圖

memento1.png

  • 基本代碼實現
/**
 * 發起人類
 */
public class Originator {

    /**
     * 狀態編碼
     */
    private String status;

    public Originator(String status) {
        this.status = status;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    /**
     * 建立備忘錄
     * @return
     */
    public Memento createMemento() {
        return new Memento(this);
    }

    /**
     * 回滾
     * @param memento
     */
    public void restoreMemento(Memento memento) {
        this.status = memento.getStatus();
    }
}

/**
 * 備忘錄類
 */
public class Memento {

    private String status;

    public Memento(Originator originator) {
        this.status = originator.getStatus();
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}

/**
 * 備忘錄管理類
 */
public class Caretaker {

    /**
     * 備忘錄記錄棧
     */
    private Stack<Memento> MEMENTO_STACK;

    public Caretaker() {
        MEMENTO_STACK = new Stack<>();
    }

    /**
     * 添加一個備忘錄
     * @param memento
     */
    public void addMemento(Memento memento) {
        MEMENTO_STACK.push(memento);
    }

    /**
     * 獲取一個備忘錄
     * @return
     */
    public Memento getMemento() {
        return MEMENTO_STACK.pop();
    }

}
  • 測試與應用
/**
 * 測試與應用
 */
public class Test {

    public static void main(String[] args) {
        //備忘錄管理
        Caretaker caretaker = new Caretaker();

        //發起人
        Originator originator = new Originator("1");

        //建立備忘錄1
        Memento memento1 = originator.createMemento();
        caretaker.addMemento(memento1);

        //修改並建立備忘錄2
        originator.setStatus("2");
        Memento memento2 = originator.createMemento();
        caretaker.addMemento(memento2);

        //修改狀態3
        originator.setStatus("3");
        System.out.println(originator.getStatus());

        //回滾上一次
        originator.restoreMemento(caretaker.getMemento());
        System.out.println(originator.getStatus());

        //回滾上一次
        originator.restoreMemento(caretaker.getMemento());
        System.out.println(originator.getStatus());
    }
}
  • 輸出結果
3
2
1
  • 備忘錄模式角色介紹html

    • 發起人(Originator)角色:記錄當前時刻的內部狀態信息,提供建立備忘錄和恢復備忘錄數據的功能,實現其餘業務功能,它能夠訪問備忘錄裏的全部信息。
    • 備忘錄(Memento)角色:負責存儲發起人的內部狀態,在須要的時候提供這些內部狀態給發起人。
    • 管理者(Caretaker)角色:對備忘錄進行管理,提供保存與獲取備忘錄的功能,但其不能對備忘錄的內容進行訪問與修改。

0x02.適用場景

  • 須要保存與恢復數據的場景,如玩遊戲時的中間結果的存檔功能。
  • 須要提供一個可回滾操做的場景,如 Word、記事本、Photoshop,Eclipse 等軟件在編輯時按 Ctrl+Z 組合鍵,還有數據庫中事務操做。

0x03.優缺點

1.優勢

  • 爲用戶提供一種可恢復機制
  • 存檔信息的封裝
  • 提供了一種能夠恢復狀態的機制。當用戶須要時可以比較方便地將數據恢復到某個歷史的狀態。
  • 實現了內部狀態的封裝。除了建立它的發起人以外,其餘對象都不可以訪問這些狀態信息。
  • 簡化了發起人類。發起人不須要管理和保存其內部狀態的各個備份,全部狀態信息都保存在備忘錄中,並由管理者進行管理,這符合單一職責原則。

2.缺點

  • 資源消耗大。若是要保存的內部狀態信息過多或者特別頻繁,將會佔用比較大的內存資源。

0x04.代碼實現

在線編輯文章時,能夠回退功能得備忘錄模式實現。
  • UML類圖

memento2.png

  • 代碼示例
/**
 * 文章類
 */
public class Article {

    //標題
    private String title;

    //內容
    private String content;

    //圖片
    private String images;

    public Article(String title, String content, String images) {
        this.title = title;
        this.content = content;
        this.images = images;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getImages() {
        return images;
    }

    public void setImages(String images) {
        this.images = images;
    }

    public ArticleMemento saveToMemento() {
        return new ArticleMemento(this);
    }

    public void undoFromMemento(ArticleMemento articleMemento) {
        this.title = articleMemento.getTitle();
        this.content = articleMemento.getContent();
        this.images = articleMemento.getImages();
    }

    @Override
    public String toString() {
        return "Article{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", images='" + images + '\'' +
                '}';
    }
}

/**
 * 備忘錄類
 */
public class ArticleMemento {

    private String title;

    private String content;

    private String images;

    public ArticleMemento(Article article) {
        this.title = article.getTitle();
        this.content = article.getContent();
        this.images = article.getImages();
    }

    public String getTitle() {
        return title;
    }

    public String getContent() {
        return content;
    }

    public String getImages() {
        return images;
    }

    @Override
    public String toString() {
        return "ArticleMemento{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", images='" + images + '\'' +
                '}';
    }
}

/**
 * 備忘錄管理類
 */
public class ArticleMementoManager {

    private final Stack<ArticleMemento> ARTICLE_MEMENTO_STACK = new Stack<>();

    public ArticleMemento getMemento () {
        return ARTICLE_MEMENTO_STACK.pop();
    }

    public void addMemento(ArticleMemento articleMemento) {
        ARTICLE_MEMENTO_STACK.push(articleMemento);
    }

}
  • 測試與應用類
/**
 * 測試與應用
 */
public class Test {

    public static void main(String[] args) {
        //建立備忘錄管理
        ArticleMementoManager articleMementoManager = new ArticleMementoManager();

        Article article = new Article("如影隨形的設計模式A", "內容A", "圖片A");

        ArticleMemento articleMemento = article.saveToMemento();

        articleMementoManager.addMemento(articleMemento);

        System.out.println(article.toString());

        article.setTitle("修改手記start");
        article.setContent("手記內容B");
        article.setImages("手記圖片B");

        System.out.println(article);

        articleMemento = article.saveToMemento();

        articleMementoManager.addMemento(articleMemento);

        article.setTitle("設計模式C");
        article.setContent("手記內容C");
        article.setImages("手記圖片C");

        System.out.println(article.toString());

        System.out.println("回退出棧一次");
        articleMemento = articleMementoManager.getMemento();
        article.undoFromMemento(articleMemento);

        System.out.println(article.toString());

        System.out.println("回退出棧兩次");
        articleMemento = articleMementoManager.getMemento();
        article.undoFromMemento(articleMemento);

        System.out.println(article.toString());
    }
}
  • 輸出結果
Article{title='design-pattern1', content='memento1', images='memento1'}
Article{title='design-pattern2', content='memento2', images='memento2'}
Article{title='design-pattern3', content='memento3', images='memento3'}
pop stack 1.
Article{title='design-pattern2', content='memento2', images='memento2'}
pop stack 2.
Article{title='design-pattern1', content='memento1', images='memento1'}

0x05.相關設計模式

  • 備忘錄模式和狀態模式java

    • 備忘錄:用實例表示狀態
    • 狀態:用類來表示狀態

0x06.源碼中的備忘錄模式

  • spring webflow: StateManageableMessageContext

0x07.代碼地址

0x08.推薦閱讀

相關文章
相關標籤/搜索