备忘录模式
定义:又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,以便以后当需要时能够将该对象恢复到原先保存的对象
备忘录模式主要结构如下:
发起人角色:纪录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息
备忘录角色:负责存储发起人的内部状态信息,在需要的时候提供这些内部状态信息给发起人
备忘录有两个等效接口
窄接口:管理者对象看到的是备忘录的窄接口,这个接口只允许它把备忘录对象传递给其他的对象
宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口,这个宽接口允许它读取备忘录中的所有数据,以便根据这些数据恢复这个发起人对象得内部状态
管理者角色:对备忘录进行管理。提供保存与 获取备忘录的功能,但其不能对备忘录的内容进行修改
案例实现
现在有一个虚拟机需要进行快照得创建(即保存当前虚拟机得状态),以便在进行错误操作之后能够进行回滚操作,要实现该案例可以通过白箱备忘录和黑箱备忘录实现。
白箱备忘录
定义发起人角色,虚拟机类
public class VirtualMachine { private String cpu; private String memory; private String username; private String password; public Memento saveState(){ return new Memento(this.cpu,this.memory,this.username,this.password); } public void recover(Memento memento){ this.cpu=memento.getCpu(); this.memory=memento.getMemory(); this.username=memento.getUsername(); this.password=memento.getPassword(); } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getMemory() { return memory; } public void setMemory(String memory) { this.memory = memory; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "VirtualMachine{" + "cpu='" + cpu + '\'' + ", memory='" + memory + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
定义备忘录角色,用于存储当前虚拟机得状态信息
public class Memento { private String cpu; private String memory; private String username; private String password; public Memento(String cpu, String memory, String username, String password) { this.cpu = cpu; this.memory = memory; this.username = username; this.password = password; } public Memento() { } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getMemory() { return memory; } public void setMemory(String memory) { this.memory = memory; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
定义管理者角色,用于对备忘录信息进行管理
public class Manage { private Memento memento; public void setMemento(Memento memento){ this.memento=memento; } public Memento getMemento(){ return this.memento; } }
测试类对白箱备忘录进行测试
public class Test { public static void main(String[] args) { VirtualMachine virtualMachine=new VirtualMachine(); virtualMachine.setCpu("15%"); virtualMachine.setMemory("20%"); virtualMachine.setUsername("zdh"); virtualMachine.setPassword("159357"); System.out.println("-------------------虚拟机初始状态------------------"); System.out.println(virtualMachine.toString()); //保存虚拟机当前状态 Memento memento=virtualMachine.saveState(); Manage manage=new Manage(); manage.setMemento(memento); System.out.println("-------------------虚拟机状态改变------------------"); virtualMachine.setCpu("100%"); virtualMachine.setMemory("100%"); virtualMachine.setUsername("asdf"); virtualMachine.setPassword("147369"); System.out.println(virtualMachine.toString()); System.out.println("-------------------虚拟机恢复初始状态------------------"); virtualMachine.recover(manage.getMemento()); System.out.println(virtualMachine.toString()); } }
在白箱备忘录模式中,备忘录向所有的对象提供的都是宽接口,即所有的对象都可以对备忘录中的数据进行访问我们可以在测试类中通过从管理者哪里获取到的备忘录调用其中的Set方法和Get方法,这样显然是破坏了封装性的,当然如果做到规范的话是可以避免的,所以白箱备忘录还是有存在的意义的
黑箱备忘录
黑箱备忘录根据上诉白箱备忘录进行改造,其他的代码不变,由于白箱备忘录对所有的对象都提供了宽接口,在黑箱备忘录中我们可以通过内部类的方式做到仅发起人角色能够访问到备忘录中的数据,其他对象例如管理者对象只拥有备忘录的窄接口即可。
定义备忘录接口
public interface Memento { }
定义发起人角色,通过内部类的方式定义备忘录角色,同时将该内部类声明为私有并实现备忘录接口,在创建备忘录的方法中我们不在返回内部类类型的对象,而是返回备忘录接口类型的对象,在恢复的方法中同样的不在接收具体的备忘录对象,而是接收备忘录接口的子实现类。
public class VirtualMachine { private String cpu; private String memory; private String username; private String password; public Memento saveState() { return new VirtualMemento(this.cpu, this.memory, this.username, this.password); } public void recover(Memento memento) { VirtualMemento memento1=(VirtualMemento) memento; this.cpu = memento1.getCpu(); this.memory = memento1.getMemory(); this.username = memento1.getUsername(); this.password = memento1.getPassword(); } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getMemory() { return memory; } public void setMemory(String memory) { this.memory = memory; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "VirtualMachine{" + "cpu='" + cpu + '\'' + ", memory='" + memory + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } /** * 私有的内部类,实现备忘录接口 */ private class VirtualMemento implements Memento { private String cpu; private String memory; private String username; private String password; public VirtualMemento(String cpu, String memory, String username, String password) { this.cpu = cpu; this.memory = memory; this.username = username; this.password = password; } public VirtualMemento() { } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getMemory() { return memory; } public void setMemory(String memory) { this.memory = memory; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } }
定义管理者角色,存储的是备忘录接口的子实现类
public class Manage { private Memento memento; public void setMemento(Memento memento){ this.memento=memento; } public Memento getMemento(){ return this.memento; } }
测试类测试黑箱备忘录
public class Test { public static void main(String[] args) { VirtualMachine virtualMachine=new VirtualMachine(); virtualMachine.setCpu("10%"); virtualMachine.setMemory("20%"); virtualMachine.setUsername("zdh"); virtualMachine.setPassword("147369"); System.out.println("---------------虚拟机初始状态---------------"); System.out.println(virtualMachine.toString()); //保存初始状态 Memento memento=virtualMachine.saveState(); Manage manage=new Manage(); manage.setMemento(memento); //改变虚拟机状态 virtualMachine.setCpu("100%"); virtualMachine.setMemory("100%"); virtualMachine.setUsername("sff"); virtualMachine.setPassword("15357"); System.out.println("---------------虚拟机状态改变---------------"); System.out.println(virtualMachine.toString()); //恢复初始状态 System.out.println("---------------恢复虚拟机初始状态---------------"); virtualMachine.recover(manage.getMemento()); System.out.println(virtualMachine.toString()); } }
通过上诉测试类中的代码,当我们获取到备忘录对象之后并不能对其中的数据进行获取或修改,这就实现了我们最终的初衷,只对发起者提供宽接口,而其他对象提供窄接口
备忘录模式优缺点
优点
提供了一种可以恢复状态的机制,当用户需要时能够比较方便的将数据恢复到某个历史的状态
实现了内部状态的封装,除了创建它的发起人之外,其他对象不能够访问这些状态信息
简化了发起人,发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这复合单一职责原则
缺点
资源消耗大,如果要保存的内部状态过多或者特别频繁,将会占用比较大的内存资源
备忘录模式的使用场景
需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能
需要提供一个可回滚操作的场景,如word,记事本,等软件还有数据库的事务操作