备忘录模式(Memento)

一、备忘录模式介绍

备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

例如:

1.office重新打开时的恢复功能。

2.事务的回滚操作

备忘录模式UML图:

Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。

Originator可根据需要决定Memento存储Originator的哪些内部状态

Memento(备忘录):负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento。

备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问返回到

先前状态所需的所有数据。

Caretaker(管理者):负责保存好备忘录Memento,不能对备忘录的内存进行操作或检查。

二、备忘录模式代码实现

以一个Emp实体对象来作为例子

首先,创建一个发起人:发起人内部保存着需要备忘的属性,它负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

//发起人:发起人内部有自身的内部状态,并且发起人可以创建备忘录和恢复备忘录

public class EmpOriginator {

    //需要备份的自身属性

    private String ename;

    private int age;

    private double salary;     

    //备份

    public EmpMemento memento(){

        return new EmpMemento(this);//将当前自身对象备份

    }     

    //恢复

    public void recovery(EmpMemento emp){

        this.ename = emp.getEname();

        this.age = emp.getAge();

        this.salary = emp.getSalary();

    }      

    //省略get,set和带参构造器  

}

创建备忘录对象:备忘录就是用来备份发起人的数据,所以构造器需要提供一个发起人对象


1

2

3

4

5

6

7

8

9

10

11

12

13

14

//备忘录对象

public class EmpMemento {

    //自身属性

    private String ename;

    private int age;

    private double salary; 

    //构造备忘录对象时,需要传入一个需要备忘的对象(发起人)

    public EmpMemento(EmpOriginator emp) {

        this.ename = emp.getEname();

        this.age = emp.getAge();

        this.salary = emp.getSalary();

    }

    //省略3个属性的set,get方法

}

开始创建一个备忘录的管理者


1

2

3

4

5

6

7

8

9

10

11

//管理者:管理备忘录对象

public class CareTaker {

    //需要管理的备忘录对象,这里也可以使用一个list容器来存储。这样可以备份多个点  

    private EmpMemento empMemento;

    public EmpMemento getEmpMemento() {

        return empMemento;

    }

    public void setEmpMemento(EmpMemento empMemento) {

        this.empMemento = empMemento;

    }  

}

单次备份测试:测试只能备份一次的备忘录


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public static void main(String[] args) {

    CareTaker taker = new CareTaker();//构建一个备忘录管理者

    //构建发起人

    EmpOriginator emp = new EmpOriginator("张三"204000);

    System.out.println("第一次:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

    //备份

    taker.setEmpMemento(emp.memento());

    

    //备份完了后再修改

    emp.setEname("李四");

    emp.setAge(30);

    emp.setSalary(50000);

    //然后再次打印

    System.out.println("修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

    

    //开始恢复

    emp.recovery(taker.getEmpMemento());

    System.out.println("恢复后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

}

测试结果如下:

第一次:张三---20---4000.0

修改后:李四---30---50000.0

恢复后:张三---20---4000.0

但是,这样的效果是只能备份一次。有时候我们需要备份多个点,根据需要来还原具体哪次的数据

多次备份

使用Stack来存储备份数据,进行多次备份。用Stack的好处是Stack是后进先出的,也就是说:你最近一次备份的数据会优先获取到

修改管理者中的代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import java.util.Stack;

//管理者:管理备忘录对象

public class CareTaker {

    //需要管理的备忘录对象,这里也可以使用一个list容器来存储。这样可以备份多个点

    //或者使用一个Stack栈来保存,因为Stack是后进先出的

    private Stack<EmpMemento> stack = new Stack<EmpMemento>();

    //备份emp数据到栈中

    public void mementoEmp(EmpMemento emp){

        stack.push(emp);

    }

    //从栈中获取最近一次备份的emp数据

    public EmpMemento getEmpForStack(){    

        if (!stack.empty()) {

            return stack.peek();//peek只获取,不删除

        }else{

            return null;

        }

    }

    //从栈中获取最近一次备份的emp数据,并且从栈中删除该数据

    public EmpMemento getEmpForStackAndRemove(){

        if (!stack.empty()) {

            return stack.pop();//pop获取后删除该元素

        }else{

            return null;

        }      

    }  

}

再次测试:后进先出,也就是说。恢复的话恢复的是最后一次备份的数据


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

public static void main(String[] args) {

    CareTaker taker = new CareTaker();//构建一个备忘录管理者

    //构建发起人

    EmpOriginator emp = new EmpOriginator("张三"204000);

    System.out.println("第一次:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

    //第1次备份

    taker.mementoEmp(emp.memento());

    

    //备份完了后再修改

    emp.setEname("李四");

    emp.setAge(30);

    emp.setSalary(50000);

    //然后再次打印

    System.out.println("1修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

    //第2次备份

    taker.mementoEmp(emp.memento());

    

    //备份完了后再修改

    emp.setEname("李四2");

    emp.setAge(32);

    emp.setSalary(52000);

    //然后再次打印

    System.out.println("2修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

    //第3次备份

    taker.mementoEmp(emp.memento());

    //备份完了后再修改

    emp.setEname("李四3");

    emp.setAge(33);

    emp.setSalary(32000);

    //然后再次打印

    System.out.println("3修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

    //第4次备份

    taker.mementoEmp(emp.memento());

    

    //开始恢复

    emp.recovery(taker.getEmpForStack());//恢复的是第4次备份的数据(3修改后)

    System.out.println("恢复后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

}

测试结果如下:

第一次:张三---20---4000.0

1修改后:李四---30---50000.0

2修改后:李四2---32---52000.0

3修改后:李四3---33---32000.0

恢复后:李四3---33---32000.0

当然,也可以恢复之后把它从栈中删除。

测试:恢复之前从栈中删除两个最近的数据


1

2

3

4

5

//开始恢复

taker.getEmpForStackAndRemove();//删除最近一次的备份

taker.getEmpForStackAndRemove();//删除最近一次的备份

emp.recovery(taker.getEmpForStack());//此时获取的是原来未删除时倒数第二次的备份

System.out.println("恢复后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());

此时的结果就是如下:可见,这里已经把2和3给删除了

第一次:张三---20---4000.0

1修改后:李四---30---50000.0

2修改后:李四2---32---52000.0

3修改后:李四3---33---32000.0

恢复后:李四---30---50000.0

三、总结

开发中常见场景:

棋类游戏中的悔棋操作

软件中的撤销操作

数据库中的事务回滚操作

常用软件中的历史记录功能

Java23种设计模式学习笔记【目录总贴】

参考资料:

  大话设计模式(带目录完整版).pdf

  HEAD_FIRST设计模式(中文版).pdf

  尚学堂_高淇_java300集最全视频教程_【GOF23设计模式】

时间: 2024-10-29 19:10:41

备忘录模式(Memento)的相关文章

设计模式之备忘录模式(Memento)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

备忘录模式(Memento Pattern)

备忘录模式(Memento Pattern)用来恢复一个对象的状态到以前的状态. 备忘录模式下属于行为模式的类别. 实现实例 在这个示例中,备忘录模式(Memento Pattern)使用三个操作者类. Memento类中包含要恢复的对象的状态. 创建者在Memento对象中创建和存储状态,Caretaker对象负责从Memento中恢复对象状态.在这个示例中,创建了以下几个类:Memento,Originator和CareTaker. MementoPatternDemo这是一个演示类,它将使

设计模式入门之备忘录模式Memento

//备忘录模式定义: //在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态. //这样以后就可以将该对象恢复到原先保存的状态 //实例:测试两种方案,两种方案在第一阶段的过程是相同的,第二阶段是不同的 //实例代码 //备忘录对象的窄接口 public interface FlowAMockMemento { //空的,所谓窄接口,即只是一个标识作用,它的持有者不可以调用任何它的方法 } //测试流程类 public class FlowAMock { private

设计模式(行为型)之备忘录模式(Memento Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(行为型)之中介者模式(Mediator Pattern)>http://blog.csdn.net/yanbober/article/details/45533335 概述 备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无

21备忘录模式Memento

一.什么是备忘录模式 Memento模式也叫备忘录模式,是行为模式之 一,它的作用是保存对象的内部状态,并在需要 的时候(undo/rollback)恢复对象以前的状态. 二.备忘录模式的应用场景 如果一个对象需要保存状态并可通过undo或rollback等 操作恢复到以前的状态时,可以使用Memento模式. 1)一个类需要保存它的对象的状态(相当于Originator角色) 2)设计一个类,该类只是用来保存上述对象的状态(相当于Memento角色) 3)需要的时候,Caretaker角色要求

[设计模式-行为型]备忘录模式(Memento)

一句话 让某个对象可以取到之前的某个状态值. 概括 解析 MEMENTO-同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦. 备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象.备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态. 实例 这个实例就以状态值

用最简单的例子理解备忘录模式(Memento Pattern)

简单来说,备忘录模式就是支持回退操作.假设让一个Notepad支持回退操作,如何实现呢? 首先需要一个备忘录类. public class Memento { private string _msg; public Memento(string msg) { _msg = msg; } public string GetText() { return _msg; } } 以上,○ 构造函数在Nodepad每次记录信息的时候调用,Nodepad所记录的信息最终通过该构造函数赋值给了_msg字段.○

[工作中的设计模式]备忘录模式memento

一.模式解析 备忘录对象是一个用来存储另外一个对象内部状态的快照的对象.备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态.备忘录模式常常与命令模式和迭代子模式一同使用. 备忘录模式可以根据客户指令,将相应的对象特有属性进行快照,如果客户要恢复对象,则根据快照提供的特有属性进行还原. 二.模式代码 package memento.patten; /** *备忘录类,同时指定要保存的对象属性

C#面向对象设计之——备忘录模式Memento Pattern(二十三)

一.前言 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象回复到原先保存的状态. 二.结构图 三.实例代码 using System; using System.Collections.Generic; using System.Text; namespace 备忘录模式 { class Program { static void Main(string[] args) { Originator o = new Originator()

【设计模式】—— 备忘录模式Memento

前言:[模式总览]——————————by xingoo 模式意图 这个模式主要是想通过一个对象来记录对象的某种状态,这样有利于在其他需要的场合进行恢复. 该模式还有跟多可以扩展的地方,比如可以记录多个时间的状态,每个角色都有可以扩展的空间,完全看业务场景而定. 应用场景 1 保存对象某一时刻的状态 2 避免直接暴露接口,破坏封装性 模式结构 Originator 是备忘录的发起者,记录状态的对象 class Originator{ private String state; public Me