备忘录模式

备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token.

适用场景

  • 保存以及恢复数据相关业务场景

  • 想要恢复到之前的状态

优点

  • 为用户提供一种可恢复的机制

  • 存档信息的封装

缺点

  • 资源占用

下面我们引入一种应用场景,在网站上发表文章的时候,会有一个暂存功能。我们能够暂时保存当前的编辑状态。同时,如果后期有必要的话,我们可以返回之前的状态。

Golang Demo

type Article struct {
  title   string
  content string
}

func NewArticle(title string, content string) *Article {
  return &Article{title: title, content: content}
}

func (a *Article) addToMemento() *ArticleMemento {
  return NewArticleMemento(a.title, a.content)
}

func (a *Article) undoFromMemento(memento *ArticleMemento) {
  a.title = memento.title
  a.content = memento.content

}
type ArticleMemento struct {
  title   string
  content string
}

func NewArticleMemento(title string, content string) *ArticleMemento {
  return &ArticleMemento{title: title, content: content}
}

type ArticleMementoManager struct {
  // 有很多种方式可以实现堆栈的效果 container/list slice 等等
  // 下面我们采用map来模拟一个不是很严谨的stack
  articleMementoStack map[int]*ArticleMemento
  index               int
}
func NewArticleMementoManager() *ArticleMementoManager {
  articleMementoStack := make(map[int]*ArticleMemento)
  return &ArticleMementoManager{articleMementoStack: articleMementoStack}
}

func (a *ArticleMementoManager) addMemento(memento *ArticleMemento) {
  a.articleMementoStack[a.index] = memento
  a.index++

}
func (a *ArticleMementoManager) getMemento() *ArticleMemento {
  if a.index > 0 {
    a.index--
    return a.articleMementoStack[a.index]
  }
  return nil

}
package memento

import "fmt"

func ExampleMemento() {
  article := NewArticle("hello", "the new world")
  articleMemento := article.addToMemento()
  articleMementoManager := NewArticleMementoManager()
  articleMementoManager.addMemento(articleMemento)

  fmt.Printf("%s,%s\n", article.title, article.content)

  article.content = "the new world2"
  fmt.Printf("%s,%s\n", article.title, article.content)

  article.undoFromMemento(articleMementoManager.getMemento())
  fmt.Printf("%s,%s\n", article.title, article.content)
  // Output:
  // hello,the new world
  // hello,the new world2
  // hello,the new world
}

Java Demo

定义一个Article 类,用来表示在网站上编写的文章。

package tech.selinux.design.pattern.behavioral.memento;

/** 网站上发表的文章 */
public class Article {
  private String title;
  private String content;

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

  public String getTitle() {
    return title;
  }

  public String getContent() {
    return content;
  }

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

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

  public ArticleMemento saveToMemento() {
    ArticleMemento articleMemento = new ArticleMemento(this.title, this.content);
    return articleMemento;
  }

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

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

编写一个备忘录类,这个类只能由article类创建,并且作为一个article的快照。这也就意味着,这个类不能被其他的类进行修改。

package tech.selinux.design.pattern.behavioral.memento;

public class ArticleMemento {
  private String title;
  private String content;

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

  public String getTitle() {
    return title;
  }

  public String getContent() {
    return content;
  }
}

定义一个备忘录管理者的类。利用栈这种数据结构来保存我们之前保存的多个状态。同时,借助栈先进后出的特性,我们能够保证最先出站的状态永远是上一次保存的状态。

package tech.selinux.design.pattern.behavioral.memento;

import java.util.Stack;

public class ArticleMementoManager {
  private final Stack<ArticleMemento> ARTICLE_MEMENTO_STACK = new Stack<ArticleMemento>();

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

  public void addMemento(ArticleMemento articleMemento) {
    ARTICLE_MEMENTO_STACK.push(articleMemento);
  }
}
package tech.selinux.design.pattern.behavioral.memento;

public class Test {
  public static void main(String[] args) {
    ArticleMementoManager articleMementoManager = new ArticleMementoManager();
    Article article = new Article("Hello", "the new world ");
    ArticleMemento articleMemento = article.saveToMemento();
    articleMementoManager.addMemento(articleMemento);
    System.out.println(article);
    article.setContent("world2");
    System.out.println(article);

    article.undoFromMemento(articleMemento);
    System.out.println(article);
  }
}

UML

补充另一个版本的Java/Scala Demo 以及源码解析

Java Demo_

Scala Demo

UML_

源码解析

Last updated