文章目录
- 一、定义:备忘录模式
- 二、模拟场景:备忘录模式
- 三、改善代码:备忘录模式
- 3.1 工程结构
- 3.2 备忘录模式模型结构图
- 3.3 备忘录模式定义
- 3.3.1 配置信息类
- 3.3.2 备忘录类
- 3.3.3 记录者类
- 3.3.4 管理员类
- 3.4 单元测试
- 四、总结:备忘录模式
一、定义:备忘录模式
- 备忘录模式:是以可以恢复或者说回滚,配置、版本、悔棋为核心功能的设计模式。
- 这种设计模式属于行为模式。
- 在功能实现上是以不破坏原对象为基础增加备忘录操作类,记录原对象的行为从而实现备忘录模式。
- 备忘录模式的使用场景:后悔药、IDEA 编辑和撤销、小霸王游戏机存档,photoshop 等
二、模拟场景:备忘录模式
- 模拟系统在发布上线的过程中记录线上配置文件用于紧急回滚。
- 在大型互联网公司系统的发布上线一定是易用、安全、可处理紧急状况的,同时为了可以隔离线上和本地环境,一般会把配置文件抽取出来放到线上,避免有人误操作导致本地的配置内容发布出去。
- 同时,线上的配置文件也会在每次变更的时候进行记录,包括:版本号、时间、MD5、内容信息和操作人。
- 在后续上线时如果发现紧急问题,系统就会需要回滚操作,如果执行回滚那么也可以设置配置文件是否回滚。
- 因为每一个版本的系统可能会带着一些配置文件的信息,这个时候就可以很方便的让系统与配置文件一起回滚操作。
- 使用备忘录模式,模拟如何记录配置文件信息。
- 在大型互联网公司系统的发布上线一定是易用、安全、可处理紧急状况的,同时为了可以隔离线上和本地环境,一般会把配置文件抽取出来放到线上,避免有人误操作导致本地的配置内容发布出去。
三、改善代码:备忘录模式
- 备忘录的设计模式实现,重点在于不更改原有类的基础上,增加备忘录类存放记录。
3.1 工程结构
design-step-18
|——src|——main|--java|--com.lino.design|--Admin.java|--ConfigFile.java|--ConfigMemento.java|--ConfigOriginator.java|--test|--java|--com.lino.design.test|--ApiTest.java
3.2 备忘录模式模型结构图
- 以上是工程结构的一个类图,其实相对来说并不复杂,除了原有的配置类
ConfigFile
以外,只新增加了三个类。- ConfigMemento:备忘录类,相当于是对原有配置类的扩展。
- ConfigOriginator:记录者类,获取和返回备忘录类对象信息。
- Admin:管理员类,用于操作记录备忘信息,比如你一些列的顺序执行了什么或者某个版本下的内容信息。
3.3 备忘录模式定义
3.3.1 配置信息类
ConfigFile.java
package com.lino.design;import java.util.Date;/*** @description: 配置文件*/
public class ConfigFile {/*** 版本号*/private String versionNo;/*** 内容*/private String content;/*** 时间*/private Date dateTime;/*** 操作人*/private String operator;public ConfigFile(String versionNo, String content, Date dateTime, String operator) {this.versionNo = versionNo;this.content = content;this.dateTime = dateTime;this.operator = operator;}public String getVersionNo() {return versionNo;}public void setVersionNo(String versionNo) {this.versionNo = versionNo;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Date getDateTime() {return dateTime;}public void setDateTime(Date dateTime) {this.dateTime = dateTime;}public String getOperator() {return operator;}public void setOperator(String operator) {this.operator = operator;}
}
- 配置类可以是任何形式的,这里只是简单的描述一个基本的配置内容信息。
3.3.2 备忘录类
ConfigMemento.java
package com.lino.design;/*** @description: 备忘录*/
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;}
}
- 备忘录是对原有配置类的扩展,可以设置和获取配置信息。
3.3.3 记录者类
ConfigOriginator.java
package com.lino.design;/*** @description: 记录者*/
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 configMemento) {this.configFile = configMemento.getConfigFile();}
}
- 记录者类除了对 ConfigFile 配置类增加了获取和设置方法外,还增加了 保存
saveMemento()
、获取getMemento(ConfigMemento configMemento)
。saveMemento()
:保存备忘录的时候会创建一个备忘录信息,并返回回去,交给管理者处理。getMemento
:获取之后并不是直接返回,而是把备忘录的信息交给现在的配置文件this.configFile
。
3.3.4 管理员类
Admin.java
package com.lino.design;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @description: 管理员*/
public class Admin {private int cursorIdx = 0;private List<ConfigMemento> mementoList = new ArrayList<>();private Map<String, ConfigMemento> mementoMap = new HashMap<>(16);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<ConfigMemento>
、Map<String, ConfigMemento>
。 - 最后是提供的备忘录操作方法。
- 存放
append()
、回滚undo()
、返回redo()
、定向获取get(String versionNo)
。
- 存放
3.4 单元测试
ApiTest.java
package com.lino.design.test;import com.alibaba.fastjson.JSON;
import com.lino.design.Admin;
import com.lino.design.ConfigFile;
import com.lino.design.ConfigOriginator;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test() {Admin admin = new Admin();ConfigOriginator configOriginator = new ConfigOriginator();configOriginator.setConfigFile(new ConfigFile("100001", "配置内容A=哈哈", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());configOriginator.setConfigFile(new ConfigFile("100002", "配置内容A=嘻嘻", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());configOriginator.setConfigFile(new ConfigFile("100003", "配置内容A=默默", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());configOriginator.setConfigFile(new ConfigFile("100004", "配置内容A=嘿嘿", new Date(), "小零哥"));admin.append(configOriginator.saveMemento());// 历史配置(回滚)configOriginator.getMemento(admin.undo());logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(回滚)configOriginator.getMemento(admin.undo());logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(前进)configOriginator.getMemento(admin.redo());logger.info("历史配置(前进)redo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(获取)configOriginator.getMemento(admin.get("100002"));logger.info("历史配置(获取)get:{}", JSON.toJSONString(configOriginator.getConfigFile()));}
}
- 这里包括了四次信息存储和备忘录历史配置操作。
- 通过上面添加了四次配置后,下面分别进行操作是:
回滚1次
,再回滚1次
,之后向前进1次
,最后是获取指定的版本配置
。
测试结果
17:04:48.903 [main] INFO com.lino.design.test.ApiTest - 历史配置(回滚)undo:{"content":"配置内容A=嘿嘿","dateTime":1675415088777,"operator":"小零哥","versionNo":"100004"}
17:04:48.913 [main] INFO com.lino.design.test.ApiTest - 历史配置(回滚)undo:{"content":"配置内容A=默默","dateTime":1675415088777,"operator":"小零哥","versionNo":"100003"}
17:04:48.913 [main] INFO com.lino.design.test.ApiTest - 历史配置(前进)redo:{"content":"配置内容A=嘿嘿","dateTime":1675415088777,"operator":"小零哥","versionNo":"100004"}
17:04:48.913 [main] INFO com.lino.design.test.ApiTest - 历史配置(获取)get:{"content":"配置内容A=嘻嘻","dateTime":1675415088777,"operator":"小零哥","versionNo":"100002"}
- 从测试结果效果上可以看到,历史配置按照我们的指令进行了回滚和前进,以及最终通过指定的版本进行获取,符合预期结果。
四、总结:备忘录模式
- 备忘录模式可以满足在不破坏原有属性类的基础上,扩充了备忘录的功能。
- 在以上的实现中我们将配置模拟存放到内存中,如果关机了会导致配置信息丢失,因为在一些真实的场景里还是需要存放到数据中。
- 此种存放到内存中进行回复的场景也不是没有,比如:Photoshop、运营人员操作 ERP 配置活动。也就是即时性的一般不需要存放到库中进行恢复。
- 另外如果是使用内存方式存放备忘录,需要考虑存储问题,避免造成内存大量消耗。