备忘录模式介绍
备忘录模式是可回复或回滚配置、以版本为核心功能的设计模式,这种设计模式属于应为模式。在功能实现上,是以不破坏原对象为基础增加备忘录操作类,记录原对象的行为,从而实现备忘录模式。
这种设计模式在日常生活或者开发中也比较常见,如编辑和撤销,以及小霸王游戏机存档。当然还有各种历史记录等。
系统上线配置回滚场景
模拟系统在发布上线的过程中记录线上配置文件用于紧急回滚。大型互联网公司系统的发布上线一定要确保过程是易用、安全、可处理紧急状况的,同时为了可以隔离线上环境和本地环境,一般会把配置文件抽取出来放到线上,避免有人因误操作导致本地配置的内容发布出去。线上的配置文件也会在每次变更时记录,包括版本、是啊金、MD5、内容和操作人等。
如果上线时出现紧急问题,系统需要执行回滚操作,可以设置配置文件是否回滚。因为每一个版本的系统可能会有一些配置文件的信息,可以很方便地让系统与配置文件一起回滚。接下来使用备忘录模式模拟如何记录配置文件信息。在实际使用过程中,还会将信息存放到数据库中保存,这里暂时只用内存记录。
备忘录模式记录系统配置
备忘录模式实现方式的重点是在不更改原有类的基础上增加备忘录类存放记录。除了这个案例,还包括日常工作中运营人员在后台ERP创建时对信息的记录,运营人员可以修改自己的版本,不会导致因为误操作而丢失信息。
备忘录模式系统服务类关系如图:

真个工程结构的类图其实不复杂,除了原有的配置类(Config),只增加了三个类。
- Admin:管理员类,用于记录备忘录信息,比如一系列的顺序执行了什么操作或者某个版本下的内容
- ConfigMeemento:备忘录,相当于对原有配置类的扩展。
- ConfigOriginator:记录者类,获取和返回备忘录类对象信息
配置信息类
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @Data@AllArgsConstructor
 @NoArgsConstructor
 public class ConfigFile {
 private String versionNo;
 private String content;
 private Date dateTime;
 private String operator;
 }
 
 | 
配置类可以是任何形式的,这里只是简单地描述了一个基本的配置信息,包括:版本号(versionNo)、配置内容(content)、配置时间(dataTime)和操作人(operator)四个核心属性。
备忘录类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | public class ConfigMemento {private ConfigFile configFile;
 public ConfigMemento(ConfigFile configFile){
 this.configFile = configFile;
 }
 
 public ConfigFile getConfigFile(){
 return configFile;
 }
 
 public void setConfigFile(ConfigFile configFile){
 this.configFile = configFile;
 }
 }
 
 | 
备忘录类是对原有配置类的扩展,可以设置和获取配置信息。
记录者类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | public class ConfigOriginator {private ConfigFile configFile;
 public ConfigFile getConfigFile(){
 return configFile;
 }
 public void setConfigFile(ConfigFile configFile){
 this.configFile = configFile;
 }
 public ConfigMemento saveMemento(){
 return new ConfigMemento(configFile);
 }
 public void getMemento(ConfigMemento memento){
 this.configFile = memento.getConfigFile();
 }
 }
 
 | 
记录者除了对ConfigFile配置类增加获取方法和设置方法,还增加了保存saveMemento()和获取getMemento(ConfigMemento memento)。saveMemento的作用在于当保存备忘录时创建一个备忘录信息,并返回给管理者处理。getMemento的作用在于获取之后不直接返回,而是把备忘录信息交给当前的配置文件this.configFile。
管理员类
| 12
 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
 
 | public class Admin {private int cursorIdx = 0;
 private List<ConfigMemento> mementoList = new ArrayList<ConfigMemento>();
 private Map<String,ConfigMemento> mementoMap = new ConcurrentHashMap<>();
 
 public void append(ConfigMemento memento){
 mementoList.add(memento);
 mementoMap.put(memento.getConfigFile().getVersionNo(),memento);
 cursorIdx++;
 }
 
 public ConfigMemento undo(){
 if (--cursorIdx <= 0){
 return mementoList.get(0);
 }
 return mementoList.get(cursorIdx);
 }
 
 public ConfigMemento redo(){
 if (++cursorIdx > mementoList.size()){
 return mementoList.get(mementoList.size() -1);
 }
 return mementoList.get(cursorIdx);
 }
 public ConfigMemento get(String versionNo){
 return mementoMap.get(versionNo);
 }
 
 
 }
 
 | 
管理员类主要实现的核心功能是记录配置文件信息,也就是备忘录的效果,提供可以回滚和获取的方法,拿到备忘录的具体内容。同时,设置了两个数据结构,用于存放备忘录List、Map<String、ConfigMemento>,在实际使用中可以按需设置。最后提供四种备忘录操作方法:存放(appen)、回滚(undo)、前进(redo)和定向获取(get)。
总结
备忘录模式可以满足在不破坏原有属性类的基础上扩充备忘录的功能。虽然和平时使用的思路一样,但在一些源码中也有所体现,值得仔细体会。在以上的实现中,是将配置模拟存到内存中,如果遇到关机,会导致配置信息丢失,因为在一些真是的场景里需要存到数据库中。而存到内存中进行回滚的场景也并非没有,例如及时性的、不需要存放到数据库中进行恢复的场合。另外,如果使用内存方式存放备忘录,需要考虑存储问题,避免造成大量消耗内存。