设计模式之备忘录模式

5次阅读

共计 5420 个字符,预计需要花费 14 分钟才能阅读完成。

0x01. 定义与类型

  • 定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
  • 类型:行为型
  • UML 类图

  • 基本代码实现
/**
 * 发起人类
 */
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
  • 备忘录模式角色介绍

    • 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
    • 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
    • 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

0x02. 适用场景

  • 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
  • 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。

0x03. 优缺点

1. 优点

  • 为用户提供一种可恢复机制
  • 存档信息的封装
  • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
  • 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
  • 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

2. 缺点

  • 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

0x04. 代码实现

在线编辑文章时,可以回退功能得备忘录模式实现。

  • UML 类图

  • 代码示例
/**
 * 文章类
 */
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. 相关设计模式

  • 备忘录模式和状态模式

    • 备忘录:用实例表示状态
    • 状态:用类来表示状态

0x06. 源码中的备忘录模式

  • spring webflow: StateManageableMessageContext

0x07. 代码地址

  • 设计模式之备忘录模式 :https://github.com/sigmaol/design-pattern/tree/master/memento

0x08. 推荐阅读

  • 慕课网设计模式精讲 :https://coding.imooc.com/class/270.html
  • 备忘录模式(详尽版):http://c.biancheng.net/view/1400.html
正文完
 0